/** * This component uses the D3 library and specifically its implementation of the force algorithm to * represent a network of protein interactions. * * @class * @extends Biojs * * @author Gustavo A. Salazar * @version 0.9.1_beta * @category 1 * * @requires jQuery Core 1.7.2 * @dependency * * @requires D3 * @dependency * * @requires InteractionsD3 CSS * @dependency * * @param {Object} options An object with the options for the InteractionsD3 component. * * @option {string} target * Identifier of the DIV tag where the component should be displayed. * @option {string} width * Width of the SVG element, if given in percentage it will use it on proportion of the container * @option {string} height * Height of the SVG element, if given in percentage it will use it on proportion of the container * @option {string} radius * Radius of the nodes representing the proteins * @option {string} enableEdges * Force the proteins to stay in the defined area of the SVG * * @example * var instance = new Biojs.InteractionsD3({ * target: "YourOwnDivId", * }); * for (var pid=1;pid<=15;pid++) * instance.addProtein({ "id":pid,"name":pid,"showLegend":false,"typeLegend":"id","organism":"human"+pid%3,"features":{"f1":"val1","f2":"val2","f3":"val3"}}); * * for (var pid=1;pid<=30;pid++) * instance.addInteraction(Math.floor((Math.random()*15)+1),Math.floor((Math.random()*15)+1) ,{score:Math.random()}); * instance.restart(); */ Biojs.InteractionsD3 = Biojs.extend ( /** @lends Biojs.InteractionsD3# */ { force:null, vis:null, interactions:[], interactionsA:{}, proteins:[], proteinsA:{}, node_drag:null, foci: [], organisms: {}, //Transformation values tTranslate:null, tScale:null, constructor: function (options) { var self = this; self.force =null; self.vis =null; self.interactions=[]; self.interactionsA={}; self.proteins=[]; self.proteinsA={}; self.node_drag=null; self.foci=[]; self.organisms={}; this._container = $("#"+self.opt.target); this._container.empty(); $(this._container).addClass("graphNetwork"); var width = $(this._container).width(), height = $(this._container).height(); if (self.opt.width.indexOf("%")!=-1) width = width*(self.opt.width.substring(0, self.opt.width.length-1)*1)/100.0; else width=self.opt.width*1; self.opt.width=width; if (self.opt.height.indexOf("%")!=-1) height = height*(self.opt.height.substring(0, self.opt.height.length-1)*1)/100.0; else height=self.opt.height*1; self.opt.height=height; this._container.width(width); this._container.height(height); self.zoom=d3.behavior.zoom(). scaleExtent([(self.opt.enableEdges)?1:0.05, 10]) .on("zoom", redraw); self.vis = d3.select("#"+self.opt.target).append("svg") .attr("width", width) .attr("height", height) .attr("pointer-events", "all") .call(self.zoom) .append('svg:g'); self.vis.append('svg:rect') .attr('width', width*20) .attr('height', height*20) .attr('x', -width*10) .attr('y', -height*10) .attr('fill', 'white') .attr('stroke','white'); self.rect= self.vis.append('svg:rect') .attr("class", "frame") .attr('width', width) .attr('height', height) .attr('fill', 'white') .attr('stroke','white') .attr("stroke-dasharray","5,5"); self.perspective=d3.select("#"+self.opt.target + " svg").append('svg:g'); function redraw(x,y,scaleP) { var trans=null,scale=null; if (typeof x!="undefined" && typeof y!="undefined"){ trans=[x,y]; scale = scaleP; }else{ trans=d3.event.translate; scale = d3.event.scale; } self.tTranslate=trans; self.tScale=scale; if (self.opt.enableEdges) { if(scale<1)scale=1; d3.behavior.zoom().scaleExtent([1, Infinity]); if (trans[0]>0)trans[0]=0; if (trans[1]>0)trans[1]=0; var W = self.rect[0][0].width.animVal.value, H= self.rect[0][0].height.animVal.value; var Ws = W*scale, Hs = H*scale; if (Ws 1e-2) && (k < 50)) { self.force.tick(); k = k + 1; } }, /** * * allows to resize the SVG element updating the gravity points * @param {string} width value of width to be assign to the SVG * @param {string} height value of height to be assign to the SVG * * @example * instance.setSize(400,400); * instance.restart(); */ setSize:function(width,height){ var self =this; self.opt.width=width; self.opt.height=height; d3.select("#"+self.opt.target+" svg") .attr('width', width) .attr('height', height); d3.select("#"+self.opt.target+" .frame") .attr('width', width) .attr('height', height); self._container.width(width); self._container.height(height); var numberOfOrganism =Object.keys(self.organisms).length; self.foci=[]; for (var i=0; ib[1]){ return -1; }else return 1; return a[0]-b[0]; }); }, _paintLegend:function(legend,type){ var self = this; legend.filter(function(d) { return d[0]== "label" && d[1]==type; }).append("text") .attr("x", self.opt.width - 6) .attr("y", 7) .attr("dy", ".35em") .style("text-anchor", "end") .style("font-size", "1.2em") .text(type+":"); if (type.indexOf("Resize By")==0){ legend.filter(function(d) { return d[0]!="label" && d[1]==type; }).append("path") .attr("class", "figure") .attr("d", function(d) { var h=2*self.opt.radius*Math.sqrt(d[0][2]); return "M0,0L0,10M0,5L"+h+",5M"+h+",0L"+h+",10 "; }) .attr("transform", function(d) { return "translate(" + (self.opt.width - 18 - 2*self.opt.radius*Math.sqrt(d[0][2])) + "," + 0 + ")"; }) .style("fill", "transparent") .style("stroke", "black"); legend.filter(function(d) { return d[0]!="label" && d[1]== type; }).append("text") .attr("x", function(d) { return (self.opt.width - 22 - 5*self.opt.radius); }) .attr("y", 7) .attr("dy", ".35em") .style("text-anchor", "end") .text(function(d) { return (d[0][1]*1.0).toFixed(2); }); }else{ legend.filter(function(d) { return d[0]!="label" && d[1]==type; }).append("rect") .attr("x", self.opt.width - 18) .attr("width", 13) .attr("height", 13) .style("fill", function(d,i) { if (typeof d[2]== "undefined") return self.colors[i]; return d[2]; }); legend.filter(function(d) { return d[0]!="label" && d[1]== type; }).append("text") .attr("x", self.opt.width - 24) .attr("y", 7) .attr("dy", ".35em") .style("text-anchor", "end") .text(function(d) { return d[0]; }); } }, _paintLegends: function(){ var self = this; var w=18 + self.longestLegend*7 + 10; var legendBlock = self.perspective.insert("g",".link") .attr("class", "legendBlock"); self._sortLegends(); legendBlock.append("rect") .attr("x", self.opt.width -w) .attr("height", 6 + self.legends.length *16) .attr("width", w) .style("fill", "#ddd") .style("fill-opacity","0.4"); var legend = legendBlock.selectAll(".mainLegend") .data(self.legends) .enter().insert("g") .attr("class", "mainLegend") .attr("transform", function(d, i) { return "translate(0," + (3 + i * 16) + ")"; }); for (var i=0; i< self.legendTypes.length; i++) self._paintLegend(legend,self.legendTypes[i]); }, longestLegend:4, legendTypes:[], /** * Adds a legend to the graphic * * @example * instance.addLegends(["Legend red"],"Color","#FF0000"); * instance.restart(); */ addLegends:function(legends,type,color){ var self = this; if (self.legends==null) self.legends=[],self.legendTypes=[]; if (legends==null) { self.legends = null; self.legendTypes=[]; self.longestLegend=4; return; } if (type=="Resize By") type = type+ " "+legends[0]; if (self.legendTypes.indexOf(type)==-1) { self.legends.push(["label",type]); self.legendTypes.push(type); if (type.length>self.longestLegend) self.longestLegend=type.length; } if (type.indexOf("Resize By")==0){ //is a size label self.legends.push([legends,type]); } else //is a color label for (var i=0;iself.longestLegend) self.longestLegend=legends[i].length; } }, _paintZoomLegend: function(){ var self = this; var w=70; var legendBlock = self.perspective.insert("g",".link") .attr("class", "legendZoomBlock"); legendBlock.append("rect") .attr("x", 0) .attr("height", 22) .attr("width", w) .style("fill", "#fff") .style("fill-opacity","0.0"); self.zoomLegend = legendBlock.selectAll(".mainLegend") .data(["1.00 :",self.tScale]) .enter().insert("g") .attr("class", "mainLegend") .attr("transform", function(d,i){ return "translate("+i*30+",15)"; }) .append("text"); self._refreshZoomLegend(); // for (var i=0; i< self.legendTypes.length; i++) // self._paintLegend(legend,self.legendTypes[i]); }, _refreshZoomLegend: function(){ var self = this; self.zoomLegend.text(function(d,i) { return (i==0)?d:(self.tScale*1.0).toFixed(2); }).attr("font-size", function(d,i){ return (i==0)?"1em":((self.tScale*1<1)?"0.8em":"1.5em"); }); }, /** * Hides the elements on the graphic that match the selector. * Check the CSS3 selectors documentation to build a selector string * * @param {string} selector a string to represent a set of elements. Check the CSS3 selectors documentation to build a selector string * * @example * instance.hide("[id = node_10]"); */ hide: function(selector){ var self=this; self.vis.selectAll(selector).attr("visibility", 'hidden'); self.vis.selectAll(selector).selectAll(" .legend").attr("visibility", 'hidden'); }, /** * Shows the elements on the graphic that match the selector. * Check the CSS3 selectors documentation to build a selector string * * @param {string} selector a string to represent a set of elements. Check the CSS3 selectors documentation to build a selector string * * @example * instance.show("[id = node_10]"); */ show: function(selector){ var self=this; self.vis.selectAll(selector).attr("visibility", 'visible'); self.vis.selectAll(selector).selectAll(" .legend").attr("visibility",function(d) { return (d.showLegend)?"visible":"hidden";}); }, /** * Highlight the elements on the graphic that match the selector. * Check the CSS3 selectors documentation to build a selector string * * @param {string} selector a string to represent a set of elements. Check the CSS3 selectors documentation to build a selector string * * @example * instance.highlight("[id *= node_1]"); */ highlight: function(selector){ var self=this; self.vis.selectAll(selector).style("stroke", '#0f0'); }, /** * Set the fill's color of the elements on the graphic that match the selector. * Check the CSS3 selectors documentation to build a selector string * * @param {string} selector a string to represent a set of elements. Check the CSS3 selectors documentation to build a selector string * @param {string} color a color in web format eg. #FF0000 * * @example * instance.setFillColor(".figure","#FF0000"); */ setFillColor: function(selector,color){ var self=this; self.vis.selectAll(selector).style("fill", color); }, /** * Set the stroke's color of the elements on the graphic that match the selector. * Check the CSS3 selectors documentation to build a selector string * * @param {string} selector a string to represent a set of elements. Check the CSS3 selectors documentation to build a selector string * @param {string} color a color in web format eg. #FF0000 * * @example * instance.setColor("[id *= node_2]","#FF0000"); */ setColor: function(selector,color){ var self=this; self.vis.selectAll(selector).style("stroke", color); }, /** * If the protein has a fixed position in the graphic it gets released, or viceversa other wise * * @param {string} protein the id of the protein to swap is position on the graphic * * @example * instance.swapFixed("3"); */ swapFixed: function(protein){ var self=this; var nodes=self.force.nodes(); nodes.forEach(function(d, i) { if (d.id==protein) d.fixed = !d.fixed; }); }, /** * Shows the legend(id) of the protein * * @param {string} protein the id of the protein to swap the visibility of the legend * * @example * instance.swapShowLegend("#node_5 .legend"); */ showLegend: function(selector,typeLegend){ var self=this; self.vis.selectAll(selector).selectAll(".legend").attr("visibility", "visible").text(function(d) { d.typeLegend=typeLegend; if (d.typeLegend=="id") return d.id; else //if (d.typeLegend.indexOf("features.")==0) return d.features[d.typeLegend]; // else // return d[d.typeLegend]; }); // self.restart(); }, /** * Scales the area of a protein * * @param {string} protein the id of the protein to scale * @param {integer} scale value to scale a node * * @example * instance.setSizeScale("#figure_1",4); */ setSizeScale: function(selector,scale){ var self=this; self.vis.selectAll(selector).attr("d", d3.svg.symbol() .size(function(d) { d.size=scale; return (2*self.opt.radius)*(2*self.opt.radius)*scale; }) .type(function(d) { return d3.svg.symbolTypes[self._figuresOrder[self.organisms[d.organism]]]; }) ); }, /** * Scales the size of the proteins which value has been modify by other means * * @param {string} selector a CSS3 selector to choose the nodes to resize * * @example * for (var i=0;i