Last active
October 19, 2018 22:48
-
-
Save edin-m/eed2af5542d60acc6bd7bdc82f9e38a6 to your computer and use it in GitHub Desktop.
L-Systems
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
</head> | |
<body> | |
<canvas id="canvas" width="600" height="600" style="border: 1px solid black"></canvas> | |
<script src="index.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const canvas = document.getElementById("canvas"); | |
const ctx = canvas.getContext('2d'); | |
const canvasSize = { width: 600, height: 600 }; | |
const lSystem = { | |
_rules: { | |
'0': '1[0]0', | |
'1': '11' | |
}, | |
_applyRules: function(ch) { | |
let newStr = ''; | |
if (this._rules[ch]) { | |
newStr = this._rules[ch]; | |
} else { | |
newStr = ch; | |
} | |
return newStr; | |
}, | |
createLSystem: function(numIter, axiom) { | |
let system = axiom; | |
while(numIter-- > 0) { | |
system = this._processString(system); | |
console.log(numIter, system); | |
} | |
return system; | |
}, | |
_processString: function(oldStr) { | |
let newStr = ''; | |
for (let i = 0; i < oldStr.length; i++) { | |
let ch = oldStr.charAt(i); | |
let rules = this._applyRules(ch); | |
newStr += rules; | |
} | |
return newStr; | |
} | |
}; | |
function Vec2d(x, y) { | |
if (!(this instanceof Vec2d)) { | |
return new Vec2d(x, y); | |
} | |
this.x = x; | |
this.y = y; | |
this.add = function(vec) { | |
this.x += vec.x; | |
this.y += vec.y; | |
return this; | |
}; | |
this.scale = function(factor) { | |
this.x *= factor; | |
this.y *= factor; | |
return this; | |
}; | |
} | |
function LSystemPainter(ctx, size, startPoint, lSystemStr) { | |
this.ctx = ctx; | |
this.size = size; | |
this.startPoint = startPoint; | |
this.lSystemStr = lSystemStr; | |
this.angleStepDeg = 45; | |
this.angleRad = toRad(this.angleStepDeg); | |
this.segmentLength = 5; | |
this.currentAngle = -90; | |
this.stack = []; | |
this._actions = { | |
'1': function() { | |
this.pos.add(vecFromAngle(this.currentAngle).scale(this.segmentLength)); | |
this.ctx.lineTo(this.pos.x, this.pos.y); | |
}, | |
'0': function() { | |
this.pos.add(vecFromAngle(this.currentAngle).scale(this.segmentLength)); | |
this.ctx.lineTo(this.pos.x, this.pos.y); | |
this.ctx.save(); | |
this.ctx.fillRect(this.pos.x - 3, this.pos.y - 3, 6, 6); | |
}, | |
'[': function() { | |
this._pushStack(this.pos); | |
}, | |
']': function() { | |
this._popStack(this.pos); | |
} | |
}; | |
this.draw = function() { | |
this.pos = Vec2d(this.startPoint.x, this.startPoint.y); | |
this.ctx.beginPath(); | |
this.ctx.moveTo(this.pos.x, this.pos.y); | |
for (let i = 0; i < this.lSystemStr.length; i++) { | |
let ch = this.lSystemStr.charAt(i); | |
if (this._actions[ch] !== null) { | |
this._actions[ch].call(this); | |
} | |
} | |
this.ctx.stroke(); | |
}; | |
this._addAngle = function(angle) { | |
this.currentAngle += angle; | |
this.currentAngle = this.currentAngle % 360; | |
}; | |
this._pushStack = function(pos) { | |
this.stack.push({ | |
currentAngle: this.currentAngle, | |
pos: { x: pos.x, y: pos.y } | |
}); | |
this._addAngle(-this.angleStepDeg); | |
}; | |
this._popStack = function() { | |
if (this.stack.length > 0) { | |
const item = this.stack.pop(); | |
this.currentAngle = item.currentAngle; | |
this.pos.x = item.pos.x; | |
this.pos.y = item.pos.y; | |
this._addAngle(this.angleStepDeg); | |
this.ctx.moveTo(this.pos.x, this.pos.y); | |
} | |
}; | |
} | |
const lSystemStr = lSystem.createLSystem(7, '0'); | |
console.log(lSystemStr); | |
const painter = new LSystemPainter(ctx, canvasSize, { x: 300, y: 598 }, lSystemStr); | |
painter.draw(); | |
// --- | |
function vecFromAngle(angle) { | |
const rad = toRad(angle); | |
return Vec2d(Math.cos(rad), Math.sin(rad)); | |
} | |
function toDeg(x) { | |
return x * 180 / Math.PI; | |
} | |
function toRad(x) { | |
return x * Math.PI / 180.0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment