我正在尝试编写一个 L-System 来生成 Penrose P3 平铺(具有薄菱形和厚菱形,我将它们称为 Rhombus-A 和 Rhombus-B。
对于渲染,我计划使用以下字符。
我的问题是 L 系统会生成 Penrose P3 平铺吗?任何参考将不胜感激。
我已经走到这一步了:
function penroseP3(){
this.a = 20; //side length
this.center = createVector(width/2, height/2)
this.theta = TWO_PI/20;
this.axiom = "[X]++++[X]++++[X]++++[X]++++[X]";
this.ruleX = "A-------[B[+++++A]+++++++++A[--[A[++++++A][------A]]][-----B][-------B[+A]][+B[+A]][+++B]++++++A]";
this.sentence = this.axiom;
this.substitute = function() {
//apply substitution rules to create new iteration of sentence string
let newSentence = "";
for (let i = 0; i < this.sentence.length; ++i) {
let step = this.sentence.charAt(i);
//if current character is 'W', replace current character
//by corresponding rule
if (step == 'W') {
newSentence = newSentence + this.ruleW; //not defined yet
} else if (step == 'X') {
newSentence = newSentence + this.ruleX;
} else if (step == 'Y') {
newSentence = newSentence + this.ruleY; //not defined yet
} else if (step == 'Z') {
newSentence = newSentence + this.ruleZ; //not defined yet
} else {
newSentence = newSentence + step; //Do nothing
}
}
this.generations++;
this.sentence = newSentence;
}
this.LSystem = function(generations) {
for (let i = 0; i < generations; i++) {
this.substitute();
}
}
this.drawRhombusA = function(){
push();
beginShape();
//bottom vertex
vertex(0, 0);
vertex(this.a * sin(2*this.theta), -this.a * cos(2*this.theta));
vertex(0, -2*this.a * cos(2*this.theta));
vertex(-this.a * sin(2*this.theta), -this.a * cos(2*this.theta));
endShape(CLOSE);
pop();
}
this.drawRhombusB = function(){
push();
beginShape();
//bottom vertex
vertex(0, 0);
vertex(this.a * sin(this.theta), -this.a * cos(this.theta));
vertex(0, -2*this.a * cos(this.theta));
vertex(-this.a * sin(this.theta), -this.a * cos(this.theta));
endShape(CLOSE);
pop();
}
this.draw = function() {
let steps = this.sentence;
// console.log(this.sentence);
translate(this.center);
for (let i = 0; i < steps.length; i++) {
let step = steps.charAt(i);
if (step == 'A') {
this.drawRhombusA();
translate(0, - 2*this.a*cos(2*this.theta));
} else if (step == 'B') {
this.drawRhombusB();
translate(this.a*sin(this.theta), -this.a*cos(this.theta));
} else if (step == '+') {
rotate(this.theta);
} else if (step == '-') {
rotate(-this.theta);
} else if (step == '[') {
push();
} else if (step == ']') {
pop();
}
}
}
}
let p3;
function setup() {
createCanvas(windowWidth, 300);
p3 = new penroseP3();
p3.LSystem(2)
}
function draw() {
background(100,200, 255);
p3.draw();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.0/p5.min.js"></script>
您还可以将代码复制/粘贴到此处以快速测试和操作代码:
经过几个小时的反复试验,我放弃了原来的方法,深入研究了数学史,发现了这个绘制菱形边缘而不是菱形形状的解决方案。由于它满足了上面所述的问题,我将其发布在这里。然而,我仍然没有找到通过绘制薄菱形和厚菱形来生成彭罗斯 P3 平铺的 L 系统的答案。
let ds;
function setup() {
createCanvas(1200, 1200);
ds = new PenroseLSystem();
//please, play around with the following line
ds.simulate(6);
}
function draw() {
background(100,200, 255);
ds.render();
}
function PenroseLSystem() {
this.steps = 0;
//these are axiom and rules for the penrose rhombus l-system
//a reference would be cool, but I couldn't find a good one
this.axiom = "[X]++[X]++[X]++[X]++[X]";
this.ruleW = "YF++ZF----XF[-YF----WF]++";
this.ruleX = "+YF--ZF[---WF--XF]+";
this.ruleY = "-WF++XF[+++YF++ZF]-";
this.ruleZ = "--YF++++WF[+ZF++++XF]--XF";
//please play around with the following two lines
this.startLength = windowWidth;
this.theta = TWO_PI / 10.0; //36 degrees, try TWO_PI / 6.0, ...
this.reset();
}
PenroseLSystem.prototype.simulate = function (gen) {
while (this.getAge() < gen) {
this.iterate(this.production);
}
}
PenroseLSystem.prototype.reset = function () {
this.production = this.axiom;
this.drawLength = this.startLength;
this.generations = 0;
}
PenroseLSystem.prototype.getAge = function () {
return this.generations;
}
//apply substitution rules to create new iteration of production string
PenroseLSystem.prototype.iterate = function() {
let newProduction = "";
for(let i=0; i < this.production.length; ++i) {
let step = this.production.charAt(i);
//if current character is 'W', replace current character
//by corresponding rule
if (step == 'W') {
newProduction = newProduction + this.ruleW;
}
else if (step == 'X') {
newProduction = newProduction + this.ruleX;
}
else if (step == 'Y') {
newProduction = newProduction + this.ruleY;
}
else if (step == 'Z') {
newProduction = newProduction + this.ruleZ;
}
else {
//drop all 'F' characters, don't touch other
//characters (i.e. '+', '-', '[', ']'
if (step != 'F') {
newProduction = newProduction + step;
}
}
}
this.drawLength = this.drawLength * 0.5;
this.generations++;
this.production = newProduction;
}
//convert production string to a turtle graphic
PenroseLSystem.prototype.render = function () {
translate(width / 2, height / 2);
// this.steps += 20;
// if(this.steps > this.production.length) {
this.steps = this.production.length;
// }
for(let i=0; i<this.steps; ++i) {
let step = this.production.charAt(i);
//'W', 'X', 'Y', 'Z' symbols don't actually correspond to a turtle action
if( step == 'F') {
stroke(255, 60);
for(let j=0; j < this.repeats; j++) {
line(0, 0, 0, -this.drawLength);
noFill();
translate(0, -this.drawLength);
}
this.repeats = 1;
}
else if (step == '+') {
rotate(this.theta);
}
else if (step == '-') {
rotate(-this.theta);
}
else if (step == '[') {
push();
}
else if (step == ']') {
pop();
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.0/p5.min.js"></script>