UNPKG

clustergrammer

Version:

This is a clustergram implemented in D3.js. I started from the example http://bost.ocks.org/mike/miserables/ and added the following features

304 lines (248 loc) 10.9 kB
var utils = require('../Utils_clust'); var label_constrain_and_trim = require('../labels/label_constrain_and_trim'); var show_visible_area = require('./show_visible_area'); var ini_zoom_info = require('../zoom/ini_zoom_info'); var toggle_grid_lines = require('../matrix/toggle_grid_lines'); module.exports = function two_translate_zoom(params, pan_dx, pan_dy, fin_zoom) { function show_crop_buttons(inst_rc, inst_button_opacity){ d3.selectAll(params.root+' .'+inst_rc+'_dendro_crop_buttons') .transition() .duration(search_duration) .style('opacity', inst_button_opacity); } d3.selectAll(params.viz.root_tips) .style('display','none'); params.zoom_info = ini_zoom_info(); show_visible_area(params); // do not allow while transitioning, e.g. reordering if (!params.viz.run_trans) { // define the commonly used variable half_height var half_height = params.viz.clust.dim.height / 2; // y pan room, the pan room has to be less than half_height since // zooming in on a gene that is near the top of the clustergram also causes // panning out of the visible region var y_pan_room = half_height / params.viz.zoom_switch; // prevent visualization from panning down too much // when zooming into genes near the top of the clustergram if (pan_dy >= half_height - y_pan_room) { // explanation of panning rules ///////////////////////////////// // prevent the clustergram from panning down too much // if the amount of panning is equal to the half_height then it needs to be reduced // effectively, the the visualization needs to be moved up (negative) by some factor // of the half-width-of-the-visualization. // // If there was no zooming involved, then the // visualization would be centered first, then panned to center the top term // this would require a // correction to re-center it. However, because of the zooming the offset is // reduced by the zoom factor (this is because the panning is occurring on something // that will be zoomed into - this is why the pan_dy value is not scaled in the two // translate transformations, but it has to be scaled afterwards to set the translate // vector) // pan_dy = half_height - (half_height)/params.viz.zoom_switch // if pan_dy is greater than the pan room, then panning has to be restricted // start by shifting back up (negative) by half_height/params.viz.zoom_switch then shift back down // by the difference between half_height and pan_dy (so that the top of the clustergram is // visible) var shift_top_viz = half_height - pan_dy; var shift_up_viz = -half_height / params.viz.zoom_switch + shift_top_viz; // reduce pan_dy so that the visualization does not get panned to far down pan_dy = pan_dy + shift_up_viz; } // prevent visualization from panning up too much // when zooming into genes at the bottom of the clustergram if (pan_dy < -(half_height - y_pan_room)) { shift_top_viz = half_height + pan_dy; shift_up_viz = half_height / params.viz.zoom_switch - shift_top_viz; //- move_up_one_row; // reduce pan_dy so that the visualization does not get panned to far down pan_dy = pan_dy + shift_up_viz; } // will improve this !! var zoom_y = fin_zoom; var zoom_x; if (fin_zoom <= params.viz.zoom_switch){ zoom_x = 1; } else { zoom_x = fin_zoom/params.viz.zoom_switch; } // search duration - the duration of zooming and panning var search_duration = 700; // center_y var center_y = -(zoom_y - 1) * half_height; // transform clust group //////////////////////////// d3.select(params.root+' .clust_group') .transition().duration(search_duration) // first apply the margin transformation // then zoom, then apply the final transformation .attr('transform', 'translate(' + [0, 0 + center_y] + ')' + ' scale(' + zoom_x + ',' + zoom_y + ')' + 'translate(' + [pan_dx, pan_dy ] + ')'); // transform row labels d3.select(params.root+' .row_label_zoom_container') .transition() .duration(search_duration) .attr('transform', 'translate(' + [0, center_y] + ')' + ' scale(' + zoom_y + ',' + zoom_y + ')' + 'translate(' + [0, pan_dy] + ')'); // transform row_cat_container // use the offset saved in params, only zoom in the y direction d3.select(params.root+' .row_cat_container') .transition() .duration(search_duration) .attr('transform', 'translate(' + [0, center_y] + ')' + ' scale(' + 1 + ',' + zoom_y + ')' + 'translate(' + [0, pan_dy] + ')'); d3.select(params.root+' .row_dendro_container') .transition() .duration(search_duration) .attr('transform', 'translate(' + [0, center_y] + ')' + ' scale(' + zoom_x + ',' + zoom_y + ')' + 'translate(' + [params.viz.uni_margin/2, pan_dy] + ')'); // toggle crop buttons var inst_button_opacity; _.each(['row','col'], function(inst_rc){ inst_button_opacity = d3.select(params.root+' .'+inst_rc+'_dendro_crop_buttons') .style('opacity'); d3.selectAll(params.root+' .'+inst_rc+'_dendro_crop_buttons') .style('opacity',0); setTimeout(show_crop_buttons, 700, inst_rc, inst_button_opacity); }); // transform col labels d3.select(params.root+' .col_zoom_container') .transition() .duration(search_duration) .attr('transform', ' scale(' + zoom_x + ',' + zoom_x + ')' + 'translate(' + [ pan_dx, 0 ] + ')'); // transform col_class d3.select(params.root+' .col_cat_container') .transition() .duration(search_duration) .attr('transform', ' scale(' + zoom_x + ',' + 1 + ')' + 'translate(' + [ pan_dx, 0 ] + ')'); d3.select(params.root+' .col_dendro_container') .transition() .duration(search_duration) .attr('transform', ' scale(' + zoom_x + ',' + 1 + ')' + 'translate(' + [ pan_dx, params.viz.uni_margin/2 ] + ')'); // set y translate: center_y is positive, positive moves the visualization down // the translate vector has the initial margin, the first y centering, and pan_dy // times the scaling zoom_y var net_y_offset = params.viz.clust.margin.top + center_y + pan_dy * zoom_y; var net_x_offset = params.viz.clust.margin.left + pan_dx; // reset the zoom and translate params.zoom_behavior .scale(zoom_y) .translate([net_x_offset, net_y_offset]); label_constrain_and_trim(params); // re-size of the highlighting rects ///////////////////////////////////////// d3.select(params.root+' .row_label_zoom_container') .each(function() { // get the bounding box of the row label text var bbox = d3.select(this) .select('text')[0][0] .getBBox(); // use the bounding box to set the size of the rect d3.select(this) .select('rect') .attr('x', bbox.x * 0.5) .attr('y', 0) .attr('width', bbox.width * 0.5) .attr('height', params.viz.y_scale.rangeBand()) .style('fill', 'yellow'); }); // reset crop button zooming d3.select(params.root+' .row_dendro_icons_group') .attr('transform', 'translate(' + [0, 0 + center_y] + ')' + ' scale(' + zoom_x + ',' + zoom_y + ')' + 'translate(' + [pan_dx, pan_dy ] + ')'); d3.select(params.root+' .row_dendro_icons_group') .selectAll('path') .attr('transform', function(d){ var inst_x = params.viz.uni_margin; var inst_y = d.pos_mid; return 'translate('+ inst_x +',' + inst_y + ') ' + 'scale(1, '+ 1/zoom_y +')'; }); d3.select(params.root+' .col_dendro_icons_group') .attr('transform', 'translate(' + [0, 0 + center_y] + ')' + ' scale(' + zoom_x + ',' + zoom_y + ')' + 'translate(' + [pan_dx, pan_dy ] + ')'); d3.select(params.root+' .col_dendro_icons_group') .selectAll('path') .attr('transform', function(d){ var inst_x = d.pos_mid; var inst_y = params.viz.uni_margin; return 'translate('+ inst_x +',' + inst_y + ') ' + 'scale('+1/zoom_x+',1)'; }); // column value bars /////////////////////// // reduce the height of the column value bars based on the zoom applied // recalculate the height and divide by the zooming scale // col_label_obj.select('rect') if (utils.has(params.network_data.col_nodes[0], 'value')) { d3.selectAll(params.root+' .col_bars') .attr('width', function(d) { var inst_value = 0; if (d.value > 0){ inst_value = params.labels.bar_scale_col(d.value)/zoom_x; } return inst_value; }); } if (utils.has(params.network_data.row_nodes[0], 'value')) { d3.selectAll(params.root+' .row_bars') .transition() .duration(search_duration) .attr('width', function(d) { var inst_value = 0; inst_value = params.labels.bar_scale_row(Math.abs(d.value))/zoom_y; return inst_value; }) .attr('x', function(d) { var inst_value = 0; inst_value = -params.labels.bar_scale_row(Math.abs(d.value))/zoom_y; return inst_value; }); } // console.log('two_translate_zoom') // console.log(params.zoom_info) // var inst_display; // d3.selectAll(params.root+' .horz_lines') // .select('line') // .attr('display',function(){ // if (params.zoom_info.zoom_y * params.viz.border_width.y > 1){ // inst_display = 'block'; // } else { // inst_display = 'none'; // } // console.log(inst_display) // return inst_display; // }) // .attr('opacity', 1); // d3.selectAll(params.root+' .vert_lines') // .select('line') // .attr('display',function(){ // if (params.zoom_info.zoom_x * params.viz.border_width.x > 1){ // inst_display = 'block'; // } else { // inst_display = 'none'; // } // console.log(inst_display) // return inst_display; // }) // .attr('opacity', 1); // console.log('HERE!!!') // if (cgm.params.zoom_info.zoom_x * cgm.params.viz.border_width.x 1){ // d3.selectAll(params.root+' .vert_lines').select('line').style('display','none'); // console.log('showing vert lines') // } // if (cgm.params.zoom_info.zoom_y * cgm.params.viz.border_width.y < 1){ // d3.selectAll(params.root+' .horz_lines').select('line').style('display','none'); // console.log('showing lines') // } toggle_grid_lines(params); } };