From 68f387bec5f59043325b043b9b6147b692587760 Mon Sep 17 00:00:00 2001 From: Michael Stauffer Date: Mon, 6 Feb 2017 10:58:28 -0500 Subject: [PATCH 1/5] basic functionality working, added [ and ] rules --- src/lsystem.js | 84 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/main.js | 6 ++-- src/turtle.js | 20 ++++++++++-- 3 files changed, 104 insertions(+), 6 deletions(-) diff --git a/src/lsystem.js b/src/lsystem.js index e643b6d..5db3063 100644 --- a/src/lsystem.js +++ b/src/lsystem.js @@ -7,26 +7,99 @@ function Rule(prob, str) { // TODO: Implement a linked list class and its requisite functions // as described in the homework writeup +export class Node { + constructor(character, prev, next){ + this.character = character; + this.prev = prev; + this.next = next; + } +} + +export class LinkedList { + constructor() { + this.head = null; + } + append( node ){ + if( this.head === null ){ + this.head = node; + return; + } + var n = this.head; + var done = false; + while( ! done ) { + if( n.next === null ){ + n.next = node; + return; + } + n = n.next; + } + } +} + +//Find the last node in a list of node, not necessarily +// a LinkedList +export function getLastNode( firstNode ) { + var lastNode = firstNode; + while( lastNode.next !== null ) + lastNode = lastNode.next; + return lastNode; +} +// Take a string and make linked nodes, +// but not a full LinkedList. +// Returns the first node +export function stringToNodes(input_string){ + var prev = null; + var first = null + for( var i=0; i < input_string.length; i++ ){ + var node = new Node( input_string.charAt(i), prev, null ); + if( first === null ) + first = node; + else + prev.next = node; + prev = node; + } + return first; +} // TODO: Turn the string into linked list -export function stringToLinkedList(input_string) { +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(); + ll.head = stringToNodes(input_string); return ll; } // TODO: Return a string form of the LinkedList -export function linkedListToString(linkedList) { +export function LinkedListToString(linkedList) { // ex. Node1("F")->Node2("X") should be "FX" var result = ""; + var node = linkedList.head; + while( node !== null ){ + result += node.character; + node = node.next; + } return result; } // TODO: Given the node to be replaced, // insert a sub-linked-list that represents replacementString function replaceNode(linkedList, node, replacementString) { + var firstNewNode = stringToNodes( replacementString ); + firstNewNode.prev = node.prev; + var prevNext = null; + if( linkedList.head === node ){ + prevNext = linkedList.head.next; + linkedList.head = firstNewNode; + } else { + prevNext = node.prev.next; + node.prev.next = firstNewNode; + } + //point to the old node's next node + var lastNewNode = getLastNode( firstNewNode ); + lastNewNode.next = prevNext; + prevNext.prev = lastNewNode; } export default function Lsystem(axiom, grammar, iterations) { @@ -71,6 +144,13 @@ export default function Lsystem(axiom, grammar, iterations) { // list of the axiom. this.doIterations = function(n) { var lSystemLL = StringToLinkedList(this.axiom); + /*console.log('axiom: ', this.axiom); + console.log('lSystemLL ', lSystemLL ); + console.log('head ', lSystemLL.head ); + console.log('last ', getLastNode(lSystemLL.head) );*/ + var str = LinkedListToString(lSystemLL); + //console.log('here'); + //console.log('lsys ll to string: ', str ); return lSystemLL; } } \ No newline at end of file diff --git a/src/main.js b/src/main.js index f0c6600..c4d785d 100644 --- a/src/main.js +++ b/src/main.js @@ -29,12 +29,14 @@ function onLoad(framework) { var lsys = new Lsystem(); turtle = new Turtle(scene); + ////// gui + gui.add(camera, 'fov', 0, 180).onChange(function(newVal) { camera.updateProjectionMatrix(); }); gui.add(lsys, 'axiom').onChange(function(newVal) { - lsys.UpdateAxiom(newVal); + lsys.updateAxiom(newVal); doLsystem(lsys, lsys.iterations, turtle); }); @@ -54,7 +56,7 @@ 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.renderSymbols(result); diff --git a/src/turtle.js b/src/turtle.js index 1db2723..65e6a6c 100644 --- a/src/turtle.js +++ b/src/turtle.js @@ -23,17 +23,22 @@ export default class Turtle { this.renderGrammar = { '+' : this.rotateTurtle.bind(this, 30, 0, 0), '-' : this.rotateTurtle.bind(this, -30, 0, 0), - 'F' : this.makeCylinder.bind(this, 2, 0.1) + 'F' : this.makeCylinder.bind(this, 2, 0.1), + '[' : this.pushState.bind(this), + ']' : this.popState.bind(this) }; } else { this.renderGrammar = grammar; } + + this.stateStack = []; } // Resets the turtle's position to the origin // and its orientation to the Y axis clear() { this.state = new TurtleState(new THREE.Vector3(0,0,0), new THREE.Vector3(0,1,0)); + this.stateStack = []; } // A function to help you debug your turtle functions @@ -43,6 +48,17 @@ export default class Turtle { console.log(this.state.dir) } + // Copy a state to stack + pushState() { + //Note that TurtleState makes new Vector3 obj's from the input, as best I can tell + this.stateStack.push( new TurtleState( this.state.pos, this.state.dir ) ); + + } + // Pop the state stack, return ref + popState() { + this.state = this.stateStack.pop(); + } + // Rotate the turtle's _dir_ vector by each of the // Euler angles indicated by the input. rotateTurtle(x, y, z) { @@ -70,7 +86,7 @@ export default class Turtle { // Moves turtle pos ahead to end of the new cylinder makeCylinder(len, width) { var geometry = new THREE.CylinderGeometry(width, width, len); - var material = new THREE.MeshBasicMaterial( {color: 0x00cccc} ); + var material = new THREE.MeshBasicMaterial( {color: 0x11cc11} ); var cylinder = new THREE.Mesh( geometry, material ); this.scene.add( cylinder ); From d6073f084b3c04ffcf9aaa2fa56a4d2e2e691fd5 Mon Sep 17 00:00:00 2001 From: mgstauffer Date: Tue, 7 Feb 2017 00:34:46 -0500 Subject: [PATCH 2/5] all basics working, need to invent some symbols --- src/lsystem.js | 130 +++++++++++++++++++++++++++++++++++++++++++++---- src/main.js | 18 +++++++ 2 files changed, 139 insertions(+), 9 deletions(-) diff --git a/src/lsystem.js b/src/lsystem.js index 5db3063..29b5eb7 100644 --- a/src/lsystem.js +++ b/src/lsystem.js @@ -1,3 +1,5 @@ +const THREE = require('three'); // older modules are imported like this. You shouldn't have to worry about this much + // A class that represents a symbol replacement rule to // be used when expanding an L-system grammar. function Rule(prob, str) { @@ -45,6 +47,14 @@ export function getLastNode( firstNode ) { return lastNode; } +//Append node B to node A +//Returns A +export function appendNode( A, B ){ + A.next = B; + B.prev = A; + return A; +} + // Take a string and make linked nodes, // but not a full LinkedList. // Returns the first node @@ -54,9 +64,14 @@ export function stringToNodes(input_string){ for( var i=0; i < input_string.length; i++ ){ var node = new Node( input_string.charAt(i), prev, null ); if( first === null ) + //Begin of list first = node; else + //Point predecessor to this new node prev.next = node; + //point the node to its predecessor + node.prev = prev; + //set up for next iteration prev = node; } return first; @@ -88,18 +103,19 @@ export function LinkedListToString(linkedList) { function replaceNode(linkedList, node, replacementString) { var firstNewNode = stringToNodes( replacementString ); firstNewNode.prev = node.prev; - var prevNext = null; + var next = null; if( linkedList.head === node ){ - prevNext = linkedList.head.next; + next = node.next; linkedList.head = firstNewNode; } else { - prevNext = node.prev.next; node.prev.next = firstNewNode; + next = node.next; } //point to the old node's next node var lastNewNode = getLastNode( firstNewNode ); - lastNewNode.next = prevNext; - prevNext.prev = lastNewNode; + lastNewNode.next = next; + if( next !== null ) + next.prev = lastNewNode; } export default function Lsystem(axiom, grammar, iterations) { @@ -107,9 +123,22 @@ export default function Lsystem(axiom, grammar, iterations) { this.axiom = "FX"; this.grammar = {}; this.grammar['X'] = [ - new Rule(1.0, '[-FX][+FX]') + new Rule(0.7, '[-FX][+FX]'), + new Rule(0.3, '[F-X+]'), + new Rule(0.0, '' ) + ]; + this.grammar['Y'] = [ + new Rule(0.0, ''), + new Rule(0.0, ''), + new Rule(0.0, '' ) + ]; + this.grammar['Z'] = [ + new Rule(0.0, ''), + new Rule(0.0, ''), + new Rule(0.0, '' ) ]; this.iterations = 0; + this.prevIterations = -1; // Set up the axiom string if (typeof axiom !== "undefined") { @@ -137,20 +166,103 @@ export default function Lsystem(axiom, grammar, iterations) { } } + this.getRandom = function(x,y){ + var vec = new THREE.Vector2(x,y); + return Math.abs( ( Math.sin( vec.dot( new THREE.Vector2(12.9898,78.233))) * 43758.5453 ) % 1 ); + } + + //Normalize rule probabilities + this.normalizeRuleProbabilities = function () { + for (var property in this.grammar) { + if (this.grammar.hasOwnProperty(property)) { + // do stuff + var ruleArr = this.grammar[property]; + var sum = 0; + for( var i = 0; i < ruleArr.length; i++ ) { + sum += ruleArr[i].probability; + }; + if( sum > 0) + for( var i = 0; i < ruleArr.length; i++ ) { + ruleArr[i].probability /= sum; + }; + } + } + + } + //For this node's character, check if there's an expansion for it + // and return the string. Otherwise just return ''. + this.getRuleExpansion = function( node, seed1, seed2 ){ + if( this.grammar[node.character] === undefined ){ + //console.log('getRuleExp: no expansion for ', node.character); + return ''; + } + //console.log('getRuleExp: expanding ', node.character); + + var date = new Date(); + seed1 += (date.getTime() % 7); + seed2 -= (date.getTime() % 11); + + //We've got a match. Choose rule variation based on probability. + //Assumes cumulative probability w/in a rule character is 1.0 + var rand = this.getRandom(seed1, seed2); // [0,1], hopefully nicely distributed + var cutoff = 0; + var result = ''; + var ruleArr = this.grammar[node.character]; + //NOTE: don't use array.ForEach here, cuz we can't break out of that loop + for( var i = 0; i < ruleArr.length; i++ ) { + var element = ruleArr[i]; + cutoff += element.probability; + if( rand <= cutoff ){ + //Make a list of linked nodes from the string + //console.log('getRuleExp - matched - element: ', element); + result = element.successorString; + //console.log('getRuleExp: chose rule ', result, ' rand, cutoff: ', rand, cutoff ); + break; + } + }; + + //should only get here when total probs for a rule set don't sum to 1.0 + if( result == '' && cutoff >= 1.0 ) + //alert('getRuleExpansion...oops!'); + console.log('WARNING: getRuleExpansion...oops! No result. cutoff, rand: ', cutoff, rand); + return result; + } + // 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) { + this.doIterations = function(numIts) { + //Get the linked list representing the axom var lSystemLL = StringToLinkedList(this.axiom); /*console.log('axiom: ', this.axiom); console.log('lSystemLL ', lSystemLL ); console.log('head ', lSystemLL.head ); console.log('last ', getLastNode(lSystemLL.head) );*/ + + //Normalize the rule probabilities + this.normalizeRuleProbabilities(); + + //Run through N interations of expanding rules + //0 iterations is just the axiom, so for n==1 we just do one iteration + for( var iter = 1; iter <= numIts; iter++ ){ + for( var node = lSystemLL.head; node !== null; node=node.next ){ + //For this node's character, check if there's an expansion for it + // and return the new node list for it. Otherwise just return this node. + var string = this.getRuleExpansion( node, iter+1, numIts ); + //console.log('doIts: string: ', string); + if( string != '' ) + replaceNode(lSystemLL, node, string); + //debug + //var str = LinkedListToString(lSystemLL); + //console.log('iteration ', iter, ' of ', numIts); + //console.log('lsys ll to string: ', str ); + } + } + var str = LinkedListToString(lSystemLL); - //console.log('here'); - //console.log('lsys ll to string: ', str ); + console.log('Final lsys ll to string: ', str ); return lSystemLL; } } \ No newline at end of file diff --git a/src/main.js b/src/main.js index c4d785d..7906864 100644 --- a/src/main.js +++ b/src/main.js @@ -37,12 +37,29 @@ function onLoad(framework) { gui.add(lsys, 'axiom').onChange(function(newVal) { lsys.updateAxiom(newVal); + clearScene(turtle); + console.log('in gui onChange axiom'); doLsystem(lsys, lsys.iterations, turtle); }); gui.add(lsys, 'iterations', 0, 12).step(1).onChange(function(newVal) { + if( lsys.prevIterations == lsys.iterations ) + return; //avoid running this twice per gui change. don't know why it happens. clearScene(turtle); + console.log('in gui onChange iterations'); doLsystem(lsys, newVal, turtle); + lsys.prevIterations = lsys.iterations; + }); + + var rules=['X','Y','Z']; + rules.forEach( function(el) { + var ruleArr = lsys.grammar[el]; + var f = gui.addFolder('Rule '+el); + for( var i=0; i < ruleArr.length; i++ ){ + var name = 'Sub '+i; + f.add(ruleArr[i], 'successorString').name(name); + f.add(ruleArr[i], 'probability', 0.0, 1.0).step(0.1).name(name+' prob'); + } }); } @@ -56,6 +73,7 @@ function clearScene(turtle) { } function doLsystem(lsystem, iterations, turtle) { + console.log('=========== doLsystem ============ '); var result = lsystem.doIterations(iterations); turtle.clear(); turtle = new Turtle(turtle.scene); From 4659eaf122d0e04a0cd251fb86d81f334f498f9c Mon Sep 17 00:00:00 2001 From: mgstauffer Date: Tue, 7 Feb 2017 16:11:59 -0500 Subject: [PATCH 3/5] ready for submission --- src/lsystem.js | 18 ++++++----- src/main.js | 34 ++++++++++++++++---- src/turtle.js | 85 +++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 109 insertions(+), 28 deletions(-) diff --git a/src/lsystem.js b/src/lsystem.js index 29b5eb7..72fb974 100644 --- a/src/lsystem.js +++ b/src/lsystem.js @@ -120,26 +120,28 @@ function replaceNode(linkedList, node, replacementString) { export default function Lsystem(axiom, grammar, iterations) { // default LSystem - this.axiom = "FX"; + this.axiom = "FA"; this.grammar = {}; - this.grammar['X'] = [ - new Rule(0.7, '[-FX][+FX]'), - new Rule(0.3, '[F-X+]'), - new Rule(0.0, '' ) + this.grammar['A'] = [ + new Rule(0.55, '[YY-CFA][YY+CFA]'), + new Rule(0.25, '[yyy--CFA][y++CFA]'), + new Rule(0.2, '[ZZcccA]' ) ]; - this.grammar['Y'] = [ + this.grammar['B'] = [ new Rule(0.0, ''), new Rule(0.0, ''), new Rule(0.0, '' ) ]; - this.grammar['Z'] = [ + this.grammar['C'] = [ new Rule(0.0, ''), new Rule(0.0, ''), new Rule(0.0, '' ) ]; - this.iterations = 0; + this.iterations = 10; this.prevIterations = -1; + this.startingRotations = [10,0,0]; + // Set up the axiom string if (typeof axiom !== "undefined") { this.axiom = axiom; diff --git a/src/main.js b/src/main.js index 7906864..372a728 100644 --- a/src/main.js +++ b/src/main.js @@ -22,12 +22,12 @@ function onLoad(framework) { scene.add(directionalLight); // set camera position - camera.position.set(1, 1, 2); - camera.lookAt(new THREE.Vector3(0,0,0)); + camera.position.set(20, 14, 4); + camera.lookAt(new THREE.Vector3(0,9,0)); // initialize LSystem and a Turtle to draw var lsys = new Lsystem(); - turtle = new Turtle(scene); + turtle = new Turtle(scene, lsys.startingRotations); ////// gui @@ -46,12 +46,16 @@ function onLoad(framework) { if( lsys.prevIterations == lsys.iterations ) return; //avoid running this twice per gui change. don't know why it happens. clearScene(turtle); - console.log('in gui onChange iterations'); + //console.log('in gui onChange iterations'); doLsystem(lsys, newVal, turtle); lsys.prevIterations = lsys.iterations; }); - var rules=['X','Y','Z']; + gui.add(lsys.startingRotations, '0',0,60).name('Init Rot X'); + gui.add(lsys.startingRotations, '1',0,60).name('Init Rot Y'); + gui.add(lsys.startingRotations, '2',0,60).name('Init Rot Z'); + + var rules=['A','B','C']; rules.forEach( function(el) { var ruleArr = lsys.grammar[el]; var f = gui.addFolder('Rule '+el); @@ -61,6 +65,12 @@ function onLoad(framework) { f.add(ruleArr[i], 'probability', 0.0, 1.0).step(0.1).name(name+' prob'); } }); + + + //generate! + clearScene(turtle); + doLsystem(lsys, lsys.iterations, turtle); + } // clears the scene by removing all geometries added by turtle.js @@ -74,14 +84,26 @@ function clearScene(turtle) { function doLsystem(lsystem, iterations, turtle) { console.log('=========== doLsystem ============ '); + clearScene(turtle); var result = lsystem.doIterations(iterations); turtle.clear(); - turtle = new Turtle(turtle.scene); + turtle = new Turtle(turtle.scene, lsystem.startingRotations); + //turtle.startingRotations = lsystem.startingRotations; //hack this in here turtle.renderSymbols(result); } // called on frame updates function onUpdate(framework) { +/* var date = new Date(); + var time = date.getMilliseconds(); + var period = 8000; + var phase = (time % period ) / period * 2 * 3.1415; + var x = Math.cos( phase ) * 4; + var z = Math.sin (phase ) * 4; + //console.log(time, phase, x, z); + framework.camera.position.set( x, 0, z ); + framework.camera.lookAt(new THREE.Vector3(0,0,0)); + framework.camera.updateProjectionMatrix();*/ } // when the scene is done initializing, it will call onLoad, then on frame updates, call onUpdate diff --git a/src/turtle.js b/src/turtle.js index 65e6a6c..b30df6c 100644 --- a/src/turtle.js +++ b/src/turtle.js @@ -4,40 +4,61 @@ const THREE = require('three') // The Turtle class contains one TurtleState member variable. // You are free to add features to this state class, // such as color or whimiscality -var TurtleState = function(pos, dir) { +var TurtleState = function(pos, dir, color/* val [0,1]*/, rotArr ) { + var rotCopy = []; + rotCopy[0] = rotArr[0]; + rotCopy[1] = rotArr[1]; + rotCopy[2] = rotArr[2]; + return { pos: new THREE.Vector3(pos.x, pos.y, pos.z), - dir: new THREE.Vector3(dir.x, dir.y, dir.z) + dir: new THREE.Vector3(dir.x, dir.y, dir.z), + color: color, + rot: rotCopy } } export default class Turtle { - constructor(scene, grammar) { - this.state = new TurtleState(new THREE.Vector3(0,0,0), new THREE.Vector3(0,1,0)); + constructor(scene, startingRotations, grammar) { + //this.state = new TurtleState(new THREE.Vector3(0,0,0), new THREE.Vector3(0,1,0), 0, [30,0,0]); this.scene = scene; - + this.rotStep = 4; + // 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), + '+' : this.rotateTurtle.bind(this, 1), + '-' : this.rotateTurtle.bind(this, -1), + 'X' : this.rotateChange.bind(this, [this.rotStep,0,0]), + 'x' : this.rotateChange.bind(this, [-this.rotStep,0,0]), + 'Y' : this.rotateChange.bind(this, [0,this.rotStep,0]), + 'y' : this.rotateChange.bind(this, [0,-this.rotStep,0]), + 'Z' : this.rotateChange.bind(this, [0,0,this.rotStep]), + 'z' : this.rotateChange.bind(this, [0,0,-this.rotStep]), 'F' : this.makeCylinder.bind(this, 2, 0.1), '[' : this.pushState.bind(this), - ']' : this.popState.bind(this) + ']' : this.popState.bind(this), + 'C' : this.changeColor.bind(this, 0.8), + 'c' : this.changeColor.bind(this, -0.8), }; } else { this.renderGrammar = grammar; } this.stateStack = []; + this.startingRotations = startingRotations; + //console.log('turtle ctor about to call clear'); + this.clear(); //sets new state } + // Resets the turtle's position to the origin // and its orientation to the Y axis clear() { - this.state = new TurtleState(new THREE.Vector3(0,0,0), new THREE.Vector3(0,1,0)); + //console.log('clear: startingRots: ', this.startingRotations ); + this.state = new TurtleState(new THREE.Vector3(0,0,0), new THREE.Vector3(0,1,0), 0, this.startingRotations ); this.stateStack = []; } @@ -51,7 +72,7 @@ export default class Turtle { // Copy a state to stack pushState() { //Note that TurtleState makes new Vector3 obj's from the input, as best I can tell - this.stateStack.push( new TurtleState( this.state.pos, this.state.dir ) ); + this.stateStack.push( new TurtleState( this.state.pos, this.state.dir, this.state.color, this.state.rot ) ); } // Pop the state stack, return ref @@ -59,16 +80,29 @@ export default class Turtle { this.state = this.stateStack.pop(); } + //Change the state color value (single float) by amount + changeColor( amount ) { + this.state.color += amount; + this.state.color %= 1; + //console.log('changeColor: new: ', this.state.color); + } + // Rotate the turtle's _dir_ vector by each of the // Euler angles indicated by the input. - rotateTurtle(x, y, z) { + rotateTurtle(sign) { + //console.log('rotateTurtle: state.rot ', this.state.rot, ' sign ', sign, ' x ', x); var e = new THREE.Euler( - x * 3.14/180, - y * 3.14/180, - z * 3.14/180); + this.state.rot[0] * sign * 3.14/180, + this.state.rot[1] * sign * 3.14/180, + this.state.rot[2] * sign * 3.14/180); this.state.dir.applyEuler(e); } + rotateChange( change ) { + for( var i = 0; i < change.length; i++ ) + this.state.rot[i] += change[i]; + } + // Translate the turtle along the input vector. // Does NOT change the turtle's _dir_ vector moveTurtle(x, y, z) { @@ -82,11 +116,34 @@ export default class Turtle { this.state.pos.add(newVec); }; + // Get a color based on [0,1] range. + // Return a THREE.Color obj + getColor( colorValIn ) { + //Simply color ramp. + //Domain [0,0.5] goes from min to max, and [0.5,1]] goes max to min + // to allow it to cycle if you just keep incrementing colorVal + var colorVal = Math.abs(colorValIn) % 1; + if( colorVal <= 0.5 ) + colorVal *=2; + else + colorVal = 1 - colorVal; + + //simple color ramp + var r = Math.pow( colorVal, 1 ); + var g = Math.pow( colorVal, 3 ); + var b = 1 - Math.pow( colorVal, 1); + + return new THREE.Color( r, g, b ); + } + // Make a cylinder of given length and width starting at turtle pos // Moves turtle pos ahead to end of the new cylinder makeCylinder(len, width) { var geometry = new THREE.CylinderGeometry(width, width, len); + var material = new THREE.MeshBasicMaterial( {color: 0x11cc11} ); + material.color = this.getColor( this.state.color); + var cylinder = new THREE.Mesh( geometry, material ); this.scene.add( cylinder ); From db08768cb4b920a9a85824207f930d24b127ff4b Mon Sep 17 00:00:00 2001 From: mgstauffer Date: Tue, 7 Feb 2017 16:14:23 -0500 Subject: [PATCH 4/5] update readme --- README.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5ca56be..1f832ed 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,28 @@ + +# Stauffer Notes + +Sorry, I didn't leave time for the readme. I added these turtle rendering symbols: + + '+' : this.rotateTurtle.bind(this, 1), + '-' : this.rotateTurtle.bind(this, -1), + 'X' : this.rotateChange.bind(this, [this.rotStep,0,0]), //increment rotation in X + 'x' : this.rotateChange.bind(this, [-this.rotStep,0,0]), //decrement rotation in X + 'Y' : this.rotateChange.bind(this, [0,this.rotStep,0]), + 'y' : this.rotateChange.bind(this, [0,-this.rotStep,0]), + 'Z' : this.rotateChange.bind(this, [0,0,this.rotStep]), + 'z' : this.rotateChange.bind(this, [0,0,-this.rotStep]), + 'F' : this.makeCylinder.bind(this, 2, 0.1), + '[' : this.pushState.bind(this), + ']' : this.popState.bind(this), + 'C' : this.changeColor.bind(this, 0.8), //increment color along color ramp + 'c' : this.changeColor.bind(this, -0.8), + +I'll try to update this README after class tonight + +================================================================================ + + 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) # L-System Parser @@ -64,4 +88,4 @@ Design a grammar for a new procedural plant! As the preceding parts of this assi # Publishing Your code -Running `npm run deploy` will automatically build your project and push it to gh-pages where it will be visible at `username.github.io/repo-name`. NOTE: You MUST commit AND push all changes to your MASTER branch before doing this or you may lose your work. The `git` command must also be available in your terminal or command prompt. If you're using Windows, it's a good idea to use Git Bash. \ No newline at end of file +Running `npm run deploy` will automatically build your project and push it to gh-pages where it will be visible at `username.github.io/repo-name`. NOTE: You MUST commit AND push all changes to your MASTER branch before doing this or you may lose your work. The `git` command must also be available in your terminal or command prompt. If you're using Windows, it's a good idea to use Git Bash. From 6d3260fa0ccead16041505ab7fa42955f483a24a Mon Sep 17 00:00:00 2001 From: mgstauffer Date: Tue, 7 Feb 2017 23:27:18 -0500 Subject: [PATCH 5/5] final changes for final submission. added color animation, better readme --- README.md | 32 +++++++++++++++----------------- src/lsystem.js | 2 +- src/main.js | 30 +++++++++++++++++++++++++----- src/turtle.js | 1 + 4 files changed, 42 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 1f832ed..54f3658 100644 --- a/README.md +++ b/README.md @@ -2,23 +2,21 @@ # Stauffer Notes -Sorry, I didn't leave time for the readme. I added these turtle rendering symbols: - - '+' : this.rotateTurtle.bind(this, 1), - '-' : this.rotateTurtle.bind(this, -1), - 'X' : this.rotateChange.bind(this, [this.rotStep,0,0]), //increment rotation in X - 'x' : this.rotateChange.bind(this, [-this.rotStep,0,0]), //decrement rotation in X - 'Y' : this.rotateChange.bind(this, [0,this.rotStep,0]), - 'y' : this.rotateChange.bind(this, [0,-this.rotStep,0]), - 'Z' : this.rotateChange.bind(this, [0,0,this.rotStep]), - 'z' : this.rotateChange.bind(this, [0,0,-this.rotStep]), - 'F' : this.makeCylinder.bind(this, 2, 0.1), - '[' : this.pushState.bind(this), - ']' : this.popState.bind(this), - 'C' : this.changeColor.bind(this, 0.8), //increment color along color ramp - 'c' : this.changeColor.bind(this, -0.8), - -I'll try to update this README after class tonight +The l-system is updated/redrawn only when the iterations or axiom are changed. +Otherwise it was updating too frequently and I didn't get around to adding +the fix that was offered to delay updates. + +The 'Anim period' is period that controls cylinders' widths scaling based +on their color index value. The color index value serves as a proxy for iteration count, since +I couldn't find a quick way to get iteration info into the turtle mesh objects. + +I added these turtle rendering symbols: + +> X/x - Increment/Decrement subsequent rotation angles around X +> Y/y - Increment/Decrement subsequent rotation angles around Y +> Z/z - Increment/Decrement subsequent rotation angles around Z +> C/C - Increment/Decrement the state color value (a 1D value that indexes a ramp) + ================================================================================ diff --git a/src/lsystem.js b/src/lsystem.js index 72fb974..5b0db13 100644 --- a/src/lsystem.js +++ b/src/lsystem.js @@ -125,7 +125,7 @@ export default function Lsystem(axiom, grammar, iterations) { this.grammar['A'] = [ new Rule(0.55, '[YY-CFA][YY+CFA]'), new Rule(0.25, '[yyy--CFA][y++CFA]'), - new Rule(0.2, '[ZZcccA]' ) + new Rule(0.2, '[ZZccXA]' ) ]; this.grammar['B'] = [ new Rule(0.0, ''), diff --git a/src/main.js b/src/main.js index 372a728..fdfc2fc 100644 --- a/src/main.js +++ b/src/main.js @@ -28,13 +28,19 @@ function onLoad(framework) { // initialize LSystem and a Turtle to draw var lsys = new Lsystem(); turtle = new Turtle(scene, lsys.startingRotations); - + framework.turtle = turtle; + + //period of color-based segment width cycling + framework.colorPeriod = 3000; + ////// gui gui.add(camera, 'fov', 0, 180).onChange(function(newVal) { camera.updateProjectionMatrix(); }); + gui.add(framework, 'colorPeriod', 100, 12000).name('Anim Period (msec)'); + gui.add(lsys, 'axiom').onChange(function(newVal) { lsys.updateAxiom(newVal); clearScene(turtle); @@ -94,11 +100,25 @@ function doLsystem(lsystem, iterations, turtle) { // called on frame updates function onUpdate(framework) { -/* var date = new Date(); - var time = date.getMilliseconds(); - var period = 8000; + if( turtle === undefined ) + return; + + var date = new Date(); + var time = date.getTime(); + var period = framework.colorPeriod; var phase = (time % period ) / period * 2 * 3.1415; - var x = Math.cos( phase ) * 4; + + //traverse nodes and change width + turtle.scene.traverse( function (node ) { + if ( node instanceof THREE.Mesh ) { + //node.stateColor is a hack for getting in some iteration state info in here + var thisPhase = phase + ( node.stateColor * 2 * 3.1415 ); + var scale = Math.sin( thisPhase ) + 1; + scale = scale / 6 + 0.2; + node.scale.set( scale, 1, scale ); + } + } ); +/* var x = Math.cos( phase ) * 4; var z = Math.sin (phase ) * 4; //console.log(time, phase, x, z); framework.camera.position.set( x, 0, z ); diff --git a/src/turtle.js b/src/turtle.js index b30df6c..7cf1061 100644 --- a/src/turtle.js +++ b/src/turtle.js @@ -145,6 +145,7 @@ export default class Turtle { material.color = this.getColor( this.state.color); var cylinder = new THREE.Mesh( geometry, material ); + cylinder.stateColor = this.state.color % 1; this.scene.add( cylinder ); //Orient the cylinder to the turtle's current direction