Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,44 @@

The objective of this assignment is to create an L System parser and generate interesting looking plants. Start by forking and then cloning this repository: [https://github.com/CIS700-Procedural-Graphics/Project3-LSystems](https://github.com/CIS700-Procedural-Graphics/Project3-LSystems)

# Description

**Linked List:**

I began by implementing a basic doubly-linked list in `lsystem.js` using the new ES6 class methodology. I also added a file `tests.js` (and added an npm command `npm run tests`) where I tested out the linked list functionality.

**LSystem Implementation:**

Then, I added additional methods to my linked list implementation to transform it into more of an "LSystem" use case. For example, I wrote the method `replaceNode()` that replaces a character in the linked list with its replacement rule, and the method `doIterations()`, which runs multiple iterations of the LSystem replacement process. My LSystem can also handle multiple rules with a given probability distribution.

**Turtle:**

After finishing the linked list and LSystem, I started on the turtle implementation. I added a number of member variables to my turtle (including `rotY`, `rotZ`, `flowerColor`, etc...) to allow for the turtle's rendering to be customized by the user. I added four additional grammar rules, listed below:

- `<`: Rotate in the Y direction X degrees
- `>`: Rotate in the Y direction -X degrees
- `O`: Draw a flower
- `L`: Draw a leaf

I also added THREE.JS geometries for leaves and flowers (which are essentially just basic shapes).

**GUI:**

Almost every aspect of the turtle and the LSystem can be customized in the dat.gui sidebar. The LSystem iterations, initial axiom, and rules (probabilities and replacements) can all be tweaked. Additionally, the turtle's rotations (in both Y and Z), cylinder dimensions, and all colors can be customized.

**Design technique:**

To be honest, I essentially just added and tweaked rules here and there until I was able to generate a plant I thought looked cool. I experimented with different probabilities, and tried to position leaves and flowers in reasonable looking positions.

**Screenshots:**

![Screenshot A](https://raw.githubusercontent.com/zelliott/Project3-LSystems/master/images/plant_a.png)

![Screenshot B](https://raw.githubusercontent.com/zelliott/Project3-LSystems/master/images/plant_b.png)

![Screenshot C](https://raw.githubusercontent.com/zelliott/Project3-LSystems/master/images/plant_c.png)


# L-System Parser

lsystem.js contains classes for L-system, Rule, and LinkedList. Here’s our suggested structure:
Expand Down Expand Up @@ -57,7 +95,7 @@ Using dat.GUI and the examples provided in the reference code, make some aspect

Design a grammar for a new procedural plant! As the preceding parts of this assignment are basic computer science tasks, this is where you should spend the bulk of your time on this assignment. Come up with new grammar rules and include screenshots of your plants in your README. For inspiration, take a look at Example 7: Fractal Plant in Wikipedia: https://en.wikipedia.org/wiki/L-system Your procedural plant must have the following features

1. Grow in 3D. Take advantage of three.js!
1. Grow in 3D. Take advantage of three.js!
2. Have flowers or leaves that are added as a part of the grammar
3. Variation. Different instances of your plant should look distinctly different!
4. A twist. Broccoli trees are cool and all, but we hope to see sometime a little more surprising in your grammars
Expand Down
290 changes: 290 additions & 0 deletions compiled/src/lsystem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
// A class that represents a symbol replacement rule to
// be used when expanding an L-system grammar.
'use strict';

Object.defineProperty(exports, '__esModule', {
value: true
});

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

exports.stringToLinkedList = stringToLinkedList;
exports.linkedListToString = linkedListToString;
exports['default'] = Lsystem;

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function Rule(prob, str) {
this.probability = prob; // The probability that this Rule will be used when replacing a character in the grammar string
this.successorString = str; // The string that will replace the char that maps to this Rule
}

var ListNode = function ListNode(data) {
_classCallCheck(this, ListNode);

this.prev = null;
this.next = null;
this.data = data;
};

exports.ListNode = ListNode;

var LinkedList = (function () {
function LinkedList() {
_classCallCheck(this, LinkedList);

this.head = null;
this.tail = null;
}

// TODO: Turn the string into linked list

_createClass(LinkedList, [{
key: 'add',
value: function add(node) {

if (!this.tail) {
this.head = node;
this.tail = node;

return;
}

node.prev = this.tail;
this.tail.next = node;
this.tail = node;
}
}, {
key: 'addFront',
value: function addFront(node) {
var n = this.head;

this.head = node;
node.next = n;
n.prev = node;
}
}, {
key: 'addBack',
value: function addBack(node) {
var n = this.tail;

this.tail = node;
n.next = node;
node.prev = n;
}
}, {
key: 'addAt',
value: function addAt(index, node) {
var n = this.head;
var i = 0;
var size = this.size();

if (index == 0) {
this.addFront(node);
return;
}

if (index == size) {
this.addBack(node);
return;
}

while (n) {
if (i == index) {
var prev = n.prev;
var next = n;

if (prev) {
prev.next = node;
}

node.prev = prev;
node.next = next;

if (next) {
next.prev = node;
}

return;
}

n = n.next;
i++;
}

throw new Error('Unable to add node at this index');
}
}, {
key: 'removeFront',
value: function removeFront() {
var n = this.head;

this.head = n.next;
this.head.prev = null;
}
}, {
key: 'removeBack',
value: function removeBack() {
var n = this.tail;

this.tail = n.prev;
this.tail.next = null;
}
}, {
key: 'removeAt',
value: function removeAt(index) {
var n = this.head;
var i = 0;
var size = this.size();

if (index == 0) {
this.removeFront();
return;
}

if (index == size) {
this.removeBack();
return;
}

while (n) {
if (i == index) {
var prev = n.prev;
var next = n.next;

if (prev) {
prev.next = next;
}

if (next) {
next.prev = prev;
}

return;
}

n = n.next;
i++;
}

throw new Error('Unable to remove node at this index');
}
}, {
key: 'getAt',
value: function getAt(index) {
var n = this.head;
var i = 0;

while (n) {
if (i == index) {
return n.data;
}

n = n.next;
i++;
}

throw new Error('Unable to get node at this index');
}
}, {
key: 'clear',
value: function clear() {
this.head = null;
this.tail = null;
}
}, {
key: 'size',
value: function size() {
var n = this.head;
var i = 0;

while (n) {
i++;
n = n.next;
}

return i;
}
}, {
key: 'print',
value: function print() {
var n = this.head;
var ret = [];

while (n) {
ret.push(n.data);
n = n.next;
}

return ret;
}
}]);

return LinkedList;
})();

exports.LinkedList = LinkedList;

function stringToLinkedList(input_string) {
// ex. assuming input_string = "F+X"
// you should return a linked list where the head is
// at Node('F') and the tail is at Node('X')
var ll = new LinkedList();
return ll;
}

// TODO: Return a string form of the LinkedList

function linkedListToString(linkedList) {
// ex. Node1("F")->Node2("X") should be "FX"
var result = "";
return result;
}

// TODO: Given the node to be replaced,
// insert a sub-linked-list that represents replacementString
function replaceNode(linkedList, node, replacementString) {}

function Lsystem(axiom, grammar, iterations) {
// default LSystem
this.axiom = "FX";
this.grammar = {};
this.grammar['X'] = [new Rule(1.0, '[-FX][+FX]')];
this.iterations = 0;

// Set up the axiom string
if (typeof axiom !== "undefined") {
this.axiom = axiom;
}

// Set up the grammar as a dictionary that
// maps a single character (symbol) to a Rule.
if (typeof grammar !== "undefined") {
this.grammar = Object.assign({}, grammar);
}

// Set up iterations (the number of times you
// should expand the axiom in DoIterations)
if (typeof iterations !== "undefined") {
this.iterations = iterations;
}

// A function to alter the axiom string stored
// in the L-system
this.updateAxiom = function (axiom) {
// Setup axiom
if (typeof axiom !== "undefined") {
this.axiom = axiom;
}
};

// TODO
// This function returns a linked list that is the result
// of expanding the L-system's axiom n times.
// The implementation we have provided you just returns a linked
// list of the axiom.
this.doIterations = function (n) {
var lSystemLL = StringToLinkedList(this.axiom);
return lSystemLL;
};
}
11 changes: 11 additions & 0 deletions compiled/src/tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use strict';

var _lsystemJs = require('./lsystem.js');

var list = new _lsystemJs.LinkedList();
list.add(new _lsystemJs.ListNode(0));
list.add(new _lsystemJs.ListNode(1));
list.add(new _lsystemJs.ListNode(2));

console.log(list.getAt(2));
console.log(list.size());
Binary file added images/plant_a.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/plant_b.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/plant_c.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"scripts": {
"start": "webpack-dev-server --hot --inline",
"build": "webpack",
"deploy": "node deploy.js"
"deploy": "node deploy.js",
"tests": "babel src/tests.js src/lsystem.js -d compiled && node compiled/src/tests.js"
},
"gh-pages-deploy": {
"prep": [
Expand All @@ -13,6 +14,7 @@
"dependencies": {
"dat-gui": "^0.5.0",
"gl-matrix": "^2.3.2",
"lodash": "^4.17.4",
"stats-js": "^1.0.0-alpha1",
"three": "^0.82.1",
"three-orbit-controls": "^82.1.0"
Expand Down
Loading