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
85 changes: 83 additions & 2 deletions src/lsystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,84 @@ function Rule(prob, str) {

// TODO: Implement a linked list class and its requisite functions
// as described in the homework writeup
var Node = {
symbol : '',
nextNode : null,
prevNode : null
};

function LinkedList(){
this.head = null;
this.tail = null;
this.push = function(val) {
if (!this.head) {
this.head = {symbol: val, nextNode: null, prevNode: null};
this.tail = this.head;
} else if (this.head == this.tail) {
var newNode = {symbol: val, nextNode: null, prevNode: this.head};
this.head.nextNode = newNode;
this.tail = newNode;
} else {
var newNode = {symbol: val, nextNode: null, prevNode: this.tail};
this.tail.nextNode = newNode;
this.tail = newNode;
}
};
};

// TODO: Turn the string into linked list
export 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();
for (var i = 0; i < input_string.length; i++) {
ll.push(input_string.charAt(i));
}
return ll;
}

// TODO: Return a string form of the LinkedList
export function linkedListToString(linkedList) {
// ex. Node1("F")->Node2("X") should be "FX"
var result = "";
var node = linkedList.head;
var result = '';

while (node) {
result = result.concat(node.symbol);
node = node.nextNode;
}
return result;
}

// TODO: Given the node to be replaced,
// insert a sub-linked-list that represents replacementString
function replaceNode(linkedList, node, replacementString) {
var replacementList = stringToLinkedList(replacementString);
if (linkedList.head == linkedList.tail == node) {
linkedList = replacementList;
}
else if (linkedList.head == node) {
var next = node.next;
linkedList.head = replacementList.head;
next.prevNode = replacementList.tail;
replacementList.tail.next = next;
}
else if (linkedList.tail == node) {
var prev = node.prevNode;
linkedList.tail = replacementList.tail;
prev.nextNode = replacementList.head;
replacementList.head.prevNode = prev;
} else {
var next = node.nextNode;
var prev = node.prevNode;
next.prevNode = replacementList.tail;
replacementList.tail.nextNode = next;

prev.nextNode = replacementList.head;
replacementList.head.nextNode = prev;
}
return replacementList.tail.next;
}

export default function Lsystem(axiom, grammar, iterations) {
Expand Down Expand Up @@ -64,13 +122,36 @@ export default function Lsystem(axiom, grammar, iterations) {
}
}

this.updateString = function(n) {
var stringResult = this.axiom;

for (var i = 0; i < n; i++) {
var newString = '';
for (var j = 0; j < stringResult.length; j++) {
var currentChar = stringResult.charAt(j);
if (this.grammar[currentChar]) {
newString = newString.concat(this.grammar[currentChar][0].successorString);
} else {
newString = newString.concat(currentChar);
}
}
stringResult = newString;
}

// console.log(stringResult);
return stringResult;
}


// 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);

var lSystemLL = stringToLinkedList(this.updateString(n));

return lSystemLL;
}
}
53 changes: 50 additions & 3 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,44 @@ import Turtle from './turtle.js'

var turtle;

var settings = {
angle : 30
}

function rate_limit(func) {
var running = false;
var next = undefined;

function onDone() {
running = false; // set the flag to allow the function to be called again
if (typeof next !== 'undefined') {
callFunc(next); // call the function again with the queued args
}
}

function callFunc(args) {
if (running) {
// if the function is already running, remember the arguments passed in so we can call the func with them after we're ready
next = args;
} else {
running = true; // prevent other function calls from running until we're done
next = undefined;
func.apply(func, args); // call the func with the arguments
}
}

// return a new function wrapping the function we want to rate limit
return function() {
// we use the same arguments but add the onDone callback as the last argument
var args = new Array(arguments.length + 1);
for (var i = 0; i < arguments.length; ++i) {
args[i] = arguments[i];
}
args[arguments.length] = onDone;
callFunc(args);
}
}

// called after the scene loads
function onLoad(framework) {
var scene = framework.scene;
Expand Down Expand Up @@ -34,13 +72,20 @@ function onLoad(framework) {
});

gui.add(lsys, 'axiom').onChange(function(newVal) {
lsys.UpdateAxiom(newVal);
lsys.updateAxiom(newVal);
clearScene(turtle);
doLsystem(lsys, lsys.iterations, turtle);
});

gui.add(lsys, 'iterations', 0, 12).step(1).onChange(function(newVal) {
gui.add(lsys, 'iterations', 0, 12).step(1).onChange(rate_limit(function(newVal, done) {
clearScene(turtle);
doLsystem(lsys, newVal, turtle);
done();
}));

gui.add(settings,'angle', 0, 90).onChange(function(newVal) {
clearScene(turtle);
doLsystem(lsys, lsys.iterations, turtle);
});
}

Expand All @@ -54,9 +99,11 @@ function clearScene(turtle) {
}

function doLsystem(lsystem, iterations, turtle) {
var result = lsystem.DoIterations(iterations);
var result = lsystem.doIterations(iterations);
turtle.clear();
turtle = new Turtle(turtle.scene);
turtle.angle = settings.angle;
turtle.updateAngles();
turtle.renderSymbols(result);
}

Expand Down
30 changes: 25 additions & 5 deletions src/turtle.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,37 @@ export default class Turtle {
constructor(scene, grammar) {
this.state = new TurtleState(new THREE.Vector3(0,0,0), new THREE.Vector3(0,1,0));
this.scene = scene;
this.stack = [];
this.angle = 30;

// TODO: Start by adding rules for '[' and ']' then more!
// Make sure to implement the functions for the new rules inside Turtle
if (typeof grammar === "undefined") {
this.renderGrammar = {
'+' : this.rotateTurtle.bind(this, 30, 0, 0),
'-' : this.rotateTurtle.bind(this, -30, 0, 0),
'F' : this.makeCylinder.bind(this, 2, 0.1)
'+' : this.rotateTurtle.bind(this, this.angle, 0, 0),
'-' : this.rotateTurtle.bind(this, -this.angle, 0, 0),
'F' : this.makeCylinder.bind(this, 2, 0.1),
'[' : this.saveState.bind(this),
']' : this.recoverState.bind(this)
};
} else {
this.renderGrammar = grammar;
}
}

saveState() {
this.stack.push(new TurtleState(this.state.pos, this.state.dir));
}

recoverState() {
this.state = this.stack.pop();
}

updateAngles() {
this.renderGrammar['+'] = this.rotateTurtle.bind(this, this.angle, 0, 0);
this.renderGrammar['-'] = this.rotateTurtle.bind(this, -this.angle, 0, 0);
}

// Resets the turtle's position to the origin
// and its orientation to the Y axis
clear() {
Expand All @@ -46,6 +63,7 @@ export default class Turtle {
// Rotate the turtle's _dir_ vector by each of the
// Euler angles indicated by the input.
rotateTurtle(x, y, z) {
// console.log(this.angle);
var e = new THREE.Euler(
x * 3.14/180,
y * 3.14/180,
Expand Down Expand Up @@ -96,17 +114,19 @@ export default class Turtle {
// Look in the Turtle's constructor for examples of how to bind
// functions to grammar symbols.
renderSymbol(symbolNode) {
var func = this.renderGrammar[symbolNode.character];
var func = this.renderGrammar[symbolNode.symbol];
if (func) {
func();
// this.printState();
}
};

// Invoke renderSymbol for every node in a linked list of grammar symbols.
renderSymbols(linkedList) {
var currentNode;
for(currentNode = linkedList.head; currentNode != null; currentNode = currentNode.next) {
for(currentNode = linkedList.head; currentNode != null; currentNode = currentNode.nextNode) {
this.renderSymbol(currentNode);
// console.log(currentNode.symbol);
}
}
}