fbf528b0c682bbb8a11f7c275b973a220d8bb49b ceisenhart Tue May 17 10:07:46 2016 -0700 Committing some old work, fixing up run speed and some other refactoring, refs #16341 diff --git src/hg/js/d3.dendrograms.js src/hg/js/d3.dendrograms.js index 657cc40..3f6667a 100644 --- src/hg/js/d3.dendrograms.js +++ src/hg/js/d3.dendrograms.js @@ -226,57 +226,57 @@ var legend=vis.selectAll('g.'+layer+'Legend').data(val); // Define the DOM elements var horz, vert; var first = 1; // Use this as a boolean to only draw the title and label one time. var viewerWidth = $(document).width()/3; // Scale to fit width var viewerHeight = $(document).height(); // Scale to fit height legend.enter() // Fill out the DOM elements .append('g') .attr('class',layer+ 'Legend') .attr('transform', function (d, i){ var height=legendRectSize + legendSpacing; var offset= height * colors.domain().length / 2; vert = (i * height - offset) + (radius*(1/2)) ; // All legends are the same height, the width location varies // Asign the legend width for each ring, scale to fit. if (layer=="inner") horz= 20 ; - if (layer=="middle") horz= 40 + viewerWidth/3; - if (layer=="outer") horz= 60 + 2*viewerWidth/3; + else if (layer=="middle") horz= 40 + viewerWidth/3; + else if (layer=="outer") horz= 60 + 2*viewerWidth/3; if (first) { vis.append("text")// The layer, this is either 'inner', 'middle', or 'outer'. .attr('class',layer+'LegendTitle') .style("font-size","16px") .style("font-weight","bold") .attr("transform", "translate(" + (horz-15) + "," + (vert-35)+ ")") .attr('x', legendRectSize + legendSpacing) .attr('y', legendRectSize - legendSpacing) .text(capitalizeFirstLetter(layer)); vis.append("text")// The title, this is mined from the meta data .attr('class',layer+'LegendLabel') .style("font-size","16px") .style("font-weight","bold") .attr("transform", "translate(" + (horz-15) + "," + (vert-15)+ ")") .attr('x', legendRectSize + legendSpacing) .attr('y', legendRectSize - legendSpacing) .text(function (){ if (title.length > 15){ return title.substring(0,14)+"..."; } return title; }); - first =0; + first = 0;// 1 is true, 0 is false; } return 'translate(' + horz + ',' + vert + ')'; // Actually move the legend. }); // Apply the colored rectangles. legend.append('rect') .attr('width', legendRectSize) .attr('height', legendRectSize) .style('fill', colors) .style('stroke', colors); // Give the colored rectangles associated name tags. legend.append('text') .style("font-size","12px") .style("font-weight","bold") @@ -381,53 +381,54 @@ .attr("class","btn btn-default dropdown-toggle") .attr("type", "button") .attr("data-toggle","dropdown") .html("Outer ring") .append("span") .attr("class","caret"); outerDropdown.append("ul") .attr("class","dropdown-menu") .html("<div id=outer"+treeType+"Leaves </div>"); }; d3.dendrogram.nodeColors = function(val, selector){ var vis = d3.select(selector).selectAll("svg"); var node=vis.selectAll("g.internalNode").selectAll("circle"); - if (val < 6) + if (val < 8) { node.attr("r", function(d){ if (d.name === " "){ if (val == 2) return Math.sqrt(d.kids-1); - if (val == 3) return 1; - if (val == 4) return 3; - if (val == 5) return 5; + else if (val == 3) return 1; //small + else if (val == 4) return 3; //normal + else if (val == 5) return 5; //large + else if (val == 6) return 0.5; //tiny + else if (val == 7) return 10; //huge } }); } - if (val > 5) + else { var link = vis.selectAll("path.link") .attr("stroke-width", function(d){ - if (val == 6) return ".04px"; - if (val == 7) return ".2px"; - if (val == 8) return "1px"; + if (val == 8) return ".07px"; // small + else if (val ==9) return ".2px"; // normal + else if (val == 10) return ".5px"; // large }); } }; - // README // The javascript buttons are created at runtime based on the .json file provided. This allows the .html code to be greatly // simplified at the expense of some rather confusing javascript. All functions with the prefix d3.dendrograms.js are // accessible from outside this module. All functions without the prefix can only be used within the module. // // The addLeafButton and addNodeButton functions are used within this program to write buttons, the buttons themselves use the globally available // functions d3.dendrogram.leafColors and d3.dendrogram.nodeColors to apply the colors. This is necesseary because the addLeafButton and // addNodeButton functions are writing new elements to the DOM, namely buttons. These buttons have an on click function that points to // the corresponding function in this module (d3.dendrogram.leafColors/d3.dendrogram.nodeColors), so the prefix is necessary to identify // where the function resides. // // A cool side effect/result of this is there is no buttons in the .html (and the user doesn't have to fus with them). // The buttons are all created at runtime by the javascript! addNodeButton = function (dropdownSelector, val, graph, title) { // Add a node button to the corresponding dropdown list. @@ -481,49 +482,53 @@ d3.select("#"+layer+"Dropdown").html(capitalizeFirstLetter(layer)+" ring: " + ringName); // Reset if the user clicks the Remove button. if (val == -1) d3.select("#"+layer+"Dropdown").html(capitalizeFirstLetter(layer) + " ring"); // If there is no remove button and this function is being called then generate a remove button function. if (!d3.select("#"+layer+"RemoveButton")[0][0]){ addLeafButton("Remove", -1, layer, 10, '#'+layer+'DendroLeaves', selector, offset); } // Keep track of the currently selected button so its color can be reverted when another button is clicked. if (layer == "inner") prevInBut = d3.select("#"+layer+capitalizeFirstLetter(title).replace("_","")+"Button").selectAll("button"); if (layer == "middle") prevMidBut = d3.select("#"+layer+capitalizeFirstLetter(title).replace("_","")+"Button").selectAll("button"); if (layer == "outer") prevOutBut = d3.select("#"+layer+capitalizeFirstLetter(title).replace("_","")+"Button").selectAll("button"); + if (val == -1) + { + vis.selectAll("text."+layer+"LegendTitle").remove(); + vis.selectAll("text."+layer+"LegendTitleLabel").remove(); + d3.select("#"+layer+"RemoveButton").remove(); + } + // Color the current button. d3.select("#"+layer+capitalizeFirstLetter(title).replace("_", "")+"Button").selectAll("button") .style("background","cornflowerblue"); // Update the ring color. Start by selecting all the polygons. node.selectAll("polygon") .style("fill", function(d){ // This block links the extra leaf json fields to a specific value (count). This value // is provided when the buttons are written in the main function. if (val==1) // A val of 1 indicates a distance ring, update the colors dictionary so the legend displays properly. { colors = d3.scale.category20().range(distanceRange).domain(distanceDomain); return d3.rgb(d.colorGroup); } - if (val==-1) // A val of -1 indicates a remove ring call. Kill everything. + else if (val==-1) // A val of -1 indicates a remove ring call. Kill everything. { - vis.selectAll("text."+layer+"LegendTitle").remove(); - vis.selectAll("text."+layer+"LegendTitleLabel").remove(); - d3.select("#"+layer+"RemoveButton").remove(); return "none"; } var count = 0; // Go through the json fields in each leaf node and find the corresponding field using the val variable // once found give it a new color with the d3 colors dictionary/function/object. for (var key in d) { if (d.hasOwnProperty(key)){ // Ignore several json fields that don't need meta data rings. if (key != "x" && key!="y" && key!= "name" && key!="kids" && key!="length" && key!="colorGroup" && key!="parent" && key!="depth" && key!="rootDist"){ if (count == val){ var sub = d[key]; // Format the json field. if (typeof d[key] === 'string' && d[key].length > 20) { sub = d[key].substring(0,19)+"..."; } count += 1; @@ -787,31 +792,31 @@ var tree = options.tree || d3.layout.cluster() .size([360, radius - 45]) .separation(function separation(a, b) { return (a.parent == b.parent ? 1 : 1) ; // Evenly place leaf nodes. }); var zoomListener = d3.behavior.zoom().scaleExtent([1, 10]) // Let the user zoom up to 10x .on("zoom", zoom); var diagonal = options.diagonal || radialRightAngleDiagonal(); // Makes links right angled. var scaling = options.scaling || 1; var width = viewerWidth -25, height = viewerHeight - 50; - var metaHorz = (viewerWidth)/12, + var metaHorz = 50, metaVert = (viewerHeight)/2; // Setup the svg for the radial display. var vis = options.vis || d3.select(selector) .attr("style", "display:inline-flex") .append("svg") .attr("width", 3*width/5 ) .attr("height", height) .attr("class", "overlay") .call(zoomListener); // Setup the svg for the control display. var legendVis = options.vis || d3.select(selector) .append("svg") .style("background","rgb(239,239,239)") @@ -860,36 +865,38 @@ .attr("stroke", "black") .attr("stroke-width", ".2px") .attr("d", diagonal); var first = 1; // Essentially a boolean. var leafCount; // The total number of leaves, will be used to calculate the polygon width. var innerOffset, middleOffset, outerOffset, rawOffset; // The offsets are used to center the polygons. var inR=6, midR=13, outR=20; // This is the pixel offset for each ring. var filled = 1; // Essentially a boolean for the first tooltip trigger. var prevSelLeaf, prevSelLeafNode; // Keep track of the previously selected node so that its color can be reverted when another node is clicked. makeDropdownSkeleton("Dendro"); //Looks for the div 'dropdown' and generates the outer list and buttons // Add a bunch of the default options for to the general commands dropdown. addNodeButton("#interiorDendroNodes" ,2, selector, "Node size: sqrt(kids)"); + addNodeButton("#interiorDendroNodes" ,6, selector, "Node size: tiny"); addNodeButton("#interiorDendroNodes" ,3, selector, "Node size: small"); addNodeButton("#interiorDendroNodes" ,4, selector, "Node size: normal"); addNodeButton("#interiorDendroNodes" ,5, selector, "Node size: large"); - addNodeButton("#interiorDendroNodes" ,6, selector, "Link width: thin"); - addNodeButton("#interiorDendroNodes" ,7, selector, "Link width: normal"); - addNodeButton("#interiorDendroNodes" ,8, selector, "Link width: thick"); + addNodeButton("#interiorDendroNodes" ,7, selector, "Node size: huge"); + addNodeButton("#interiorDendroNodes" ,8, selector, "Link width: thin"); + addNodeButton("#interiorDendroNodes" ,9, selector, "Link width: normal"); + addNodeButton("#interiorDendroNodes" ,10, selector, "Link width: thick"); // These are true nodes in the sense that they are actually calculated and defined, // however this code is not repsonsible for the actual visuale representation. This // block only handles the backend DOM construction, the shapes and colors are assigned later. var trueNodes = svgGroup.selectAll("g") .data(nodes).enter() .append("g") .attr("class", function(n) { if (n.depth===0){ // This is the root, do a bunch of one off things. leafCount = n.kids; rawOffset = Math.PI*2*(radius)/(parseInt(leafCount)*3); innerOffset = Math.PI*2*(radius+inR)/(parseInt((leafCount*(scaling))*2)); middleOffset = Math.PI*2*(radius+midR)/(parseInt((leafCount*(scaling))*2)); outerOffset = Math.PI*2*(radius+outR)/(parseInt((leafCount*(scaling))*2)); @@ -916,31 +923,31 @@ } first = 0; } return "leaf"; } }) .attr("transform", function(d){return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")";}); // This section handles the internal node visual representation, including the node initial size/color // and the tooltips (on click visualizations) var interiorNodes = svgGroup.selectAll("g.internalNode") .append("circle") .attr("r", function(d){ // Handle node size if (d.name==" "){ - return Math.sqrt(d.kids-1);} + return 3;} else{return 0;} }) .style("fill", function(d){ // Handle node color if (d.name==" "){return d.whiteToBlack;} else{return "none";}}) .on("click", function(d) { // Tooltip stuffs d3.select(this).style("fill", "red"); // Color this node red if (filled == 2) { // Another node was previously clicked, its color needs to be reverted. if (d3.select(this) != prevSelLeaf && d3.select(this) != prevSelLeafNode && d != prevSelLeaf && d != prevSelLeafNode) { if (prevSelLeafNode.name==" "){prevSelLeaf.style("fill", prevSelLeafNode.whiteToBlack);} else{prevSelLeaf.style("fill", "white");} } } tooltip.transition()