1// Note: this is the old (NO LONGER WORKING) implementation of the tree diagram drawn using d3. 2 3//This function builds the tree using d3 (http://d3js.org/) 4//A good video explaining the tree: 5//http://www.youtube.com/watch?v=x8dwXoWODZ4 (part 1) 6//http://www.youtube.com/watch?v=iZ6MSHA4FMU (part 2) 7var treeData = {}; 8 9function buildTree() 10{ 11 //initialize tree 12 treeData = {}; 13 14 //clean up matInfo 15 cleanMatInfo(); 16 sortMatInfo(); 17 18 //calculate number of levels 19 var numberOfLevels = 1; 20 for(var i=0; i<matInfo.length; i++) { 21 var currentLevel = getNumUnderscores(matInfo[i].endtag); 22 if(currentLevel > numberOfLevels) 23 numberOfLevels = currentLevel; 24 } 25 26 for(var i=0; i<matInfo.length; i++) {//first zero doesn't matter 27 var string = getSimpleDescription(matInfo[i].endtag); 28 var obj = {name: string};//the new object to be placed 29 var endtag = matInfo[i].endtag; 30 var needsAllocation=false; 31 32 if(endtag == "0") 33 treeData=obj; 34 35 //get the last number in the endtag 36 if(endtag == "0" || endtag.substring(endtag.lastIndexOf("_")+1,endtag.length) == "0") { 37 //need to allocate 38 needsAllocation=true; 39 } 40 41 if(getNumUnderscores(endtag) == 1) { 42 if(needsAllocation) { 43 treeData.contents=[]; 44 } 45 treeData.contents[getNthPos(endtag,1)] = obj; 46 } 47 else if(getNumUnderscores(endtag) == 2) { 48 if(needsAllocation) { 49 treeData.contents[getNthPos(endtag,1)].contents=[]; 50 } 51 treeData.contents[getNthPos(endtag,1)].contents[getNthPos(endtag,2)]=obj; 52 } 53 else if(getNumUnderscores(endtag) == 3) { 54 if(needsAllocation) { 55 treeData.contents[getNthPos(endtag,1)].contents[getNthPos(endtag,2)].contents=[]; 56 } 57 treeData.contents[getNthPos(endtag,1)].contents[getNthPos(endtag,2)].contents[getNthPos(endtag,3)]=obj; 58 } 59 60 } 61 62 //--------------------------------------------------- 63 //Create a container for the tree - a 'canvas' 64 //[n*310, n* 310] for horizontal 65 //[n*580, n* 310] for vertical 66 var canvas = d3.select("#tree").append("svg") 67 .attr("width", numberOfLevels * 300) 68 .attr("height", numberOfLevels * 200) 69 .append("g") 70 .attr("transform", "translate(10,0)"); 71 72 //Call the d3 tree layout 73 var tree = d3.layout.tree() 74 .size([numberOfLevels * 150, numberOfLevels * 250]) 75 .children(function(d) //find who has children from the data structure 76 { 77 return (!d.contents || d.contents.length === 0) ? null : d.contents; 78 }); 79 80 //initialize the nodes and links (which are used by d3 to create the paths 81 var nodes = tree.nodes(treeData); 82 var links = tree.links(nodes); 83 84 //create an actual node group on the canvas (where a dot and text will be placed) 85 var node = canvas.selectAll(".node") 86 .data(nodes) 87 .enter() 88 .append("g") 89 .attr("class", "node") 90 .attr("transform", function (d){return "translate(" + d.y + "," + d.x + ")";}) //root: left 91 //.attr("transform", function (d){return "translate(" + d.x + "," + d.y + ")";}) //root: top 92 93 //add to that node a circle 94 //change the circle properties here 95 node.append("circle") 96 .attr("r", 5) 97 .attr("fill", "steelblue") 98 99 //Add text to the node (names) 100 //"foreignObject" is used to allow for rich formatting through mathJax and HTML (such as wrapping) 101 //adjust the size of the box to create the desired aesthetic 102 //Move it with x and y 103 node.append("foreignObject") 104 .attr('width',400) 105 .attr('height',400)//perhaps just remove these attributes altogether so it can resize itself as needed? 106 //.attr('x', -23) 107 .attr('requiredFeatures','http://www.w3.org/TR/SVG11/feature#Extensibility') 108 .append('xhtml') 109 .html(function (d) { return d.name; }) //this is where the data is actually placed in the tree 110 111 112 //diagonal is the d3 method that draws the lines 113 var diagonal = d3.svg.diagonal() 114 .projection(function (d) { return [d.y, d.x]}) //root: left 115 //.projection(function (d) { return [d.x, d.y]}) //root: top 116 117 //Writes everything to screen 118 canvas.selectAll(".link") 119 .data(links) 120 .enter() 121 .append("path") 122 .attr("class", "link") 123 .attr("fill", "none") 124 .attr("stroke", "#404040") 125 .attr("d", diagonal) 126} 127 128//pos goes from 0 to numUnderscores. pos 0 is always "0" 129function getNthPos(endtag,pos) 130{ 131 if(pos == 0) 132 return 0; 133 if(pos > getNumUnderscores(endtag)) 134 return -1; //error. this is not possible. 135 136 for(var i=0; i<pos; i++) { //remove 'pos' number of underscores 137 endtag = endtag.substring(endtag.indexOf("_")+1, endtag.length); 138 } 139 140 if(endtag.indexOf("_") == -1) //has no more underscores so just return everything that's left 141 return parseInt(endtag); 142 else //otherwise, return up to the next underscore 143 return parseInt(endtag.substring(0,endtag.indexOf("_"))); 144 } 145 146//remove all the -1 elements (these get generated when something is deleted) 147function cleanMatInfo() 148{ 149 for(var i=0; i<matInfo.length; i++) { 150 if(matInfo[i].endtag == "-1") { 151 152 //shift everything down 153 for(var j=i; j<matInfo.length-1; j++) 154 matInfo[j]=matInfo[j+1]; 155 156 i--;//there might be two in a row 157 158 delete matInfo[matInfo.length-1];//remove garbage value 159 matInfo.length--;//after deletion, there will be an "undefined" value there so we need this line to actually shrink the array 160 } 161 } 162} 163 164//selection sort. NOTE: THIS FUNCTION ASSUMES ALL GARBAGE VALUES (ID="-1") HAVE ALREADY BEEN REMOVED USING cleanMatInfo() 165function sortMatInfo() 166{ 167 for(var i=0; i<matInfo.length-1; i++) {//only need to go to second to last element 168 var indexOfCurrentSmallest = i; 169 for(var j=i; j<matInfo.length; j++) { 170 if(compare(matInfo[j].endtag,matInfo[indexOfCurrentSmallest].endtag) == -1) { 171 indexOfCurrentSmallest = j; 172 } 173 } 174 //swap i and indexOfCurrentSmallest 175 var temp = matInfo[i]; 176 matInfo[i] = matInfo[indexOfCurrentSmallest]; 177 matInfo[indexOfCurrentSmallest] = temp; 178 } 179} 180 181function compare(endtag1, endtag2) 182{ 183 if(endtag1 == endtag2) 184 return 0; 185 186 var min = getNumUnderscores(endtag1); 187 if(getNumUnderscores(endtag2) < min) 188 min = getNumUnderscores(endtag2); 189 190 var i=0; 191 for(i=0; i<=min; i++) { 192 if(getNthPos(endtag1,i) < getNthPos(endtag2,i)) 193 return -1; 194 if(getNthPos(endtag1,i) > getNthPos(endtag2,i)) 195 return 1; 196 } 197 198 //endtags matched up to the end of one endtag so whichever is shorter goes first 199 if(endtag1.length < endtag2.length) 200 return -1; 201 return 1; 202} 203