diff --git a/packages/oncoprintjs/dist/oncoprint.bundle.js b/packages/oncoprintjs/dist/oncoprint.bundle.js index 5a11dbb3f3b..2562d5490b2 100644 --- a/packages/oncoprintjs/dist/oncoprint.bundle.js +++ b/packages/oncoprintjs/dist/oncoprint.bundle.js @@ -10188,9 +10188,9 @@ module.exports = glMatrix; /* 2 */ /***/ (function(module, exports, __webpack_require__) { -var makeSVGElement = __webpack_require__(11); +var makeSVGElement = __webpack_require__(4); var shapeToSVG = __webpack_require__(30); -var extractRGBA = __webpack_require__(4); +var extractRGBA = __webpack_require__(5); var extractColor = function(str) { if (str.indexOf("rgb(") > -1 || str.indexOf("url(") > -1) { @@ -10378,6 +10378,20 @@ module.exports = { /* 4 */ /***/ (function(module, exports) { +module.exports = function (tag, attrs) { + var el = document.createElementNS('http://www.w3.org/2000/svg', tag); + for (var k in attrs) { + if (attrs.hasOwnProperty(k)) { + el.setAttribute(k, attrs[k]); + } + } + return el; +}; + +/***/ }), +/* 5 */ +/***/ (function(module, exports) { + module.exports = function (str) { var ret = [0, 0, 0, 1]; if (str[0] === "#") { @@ -10399,7 +10413,7 @@ module.exports = function (str) { /***/ }), -/* 5 */ +/* 6 */ /***/ (function(module, exports) { module.exports = function(array, target_key, keyFn, return_closest_lower_if_not_found) { @@ -10432,7 +10446,7 @@ module.exports = function(array, target_key, keyFn, return_closest_lower_if_not_ } /***/ }), -/* 6 */ +/* 7 */ /***/ (function(module, exports) { var CachedProperty = (function() { @@ -10463,7 +10477,7 @@ var CachedProperty = (function() { module.exports = CachedProperty; /***/ }), -/* 7 */ +/* 8 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -10497,15 +10511,15 @@ THE SOFTWARE. */ exports.glMatrix = __webpack_require__(1); exports.mat2 = __webpack_require__(25); exports.mat2d = __webpack_require__(26); -exports.mat3 = __webpack_require__(8); +exports.mat3 = __webpack_require__(9); exports.mat4 = __webpack_require__(27); exports.quat = __webpack_require__(28); exports.vec2 = __webpack_require__(29); -exports.vec3 = __webpack_require__(9); -exports.vec4 = __webpack_require__(10); +exports.vec3 = __webpack_require__(10); +exports.vec4 = __webpack_require__(11); /***/ }), -/* 8 */ +/* 9 */ /***/ (function(module, exports, __webpack_require__) { /* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV. @@ -11257,7 +11271,7 @@ module.exports = mat3; /***/ }), -/* 9 */ +/* 10 */ /***/ (function(module, exports, __webpack_require__) { /* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV. @@ -12040,7 +12054,7 @@ module.exports = vec3; /***/ }), -/* 10 */ +/* 11 */ /***/ (function(module, exports, __webpack_require__) { /* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV. @@ -12654,20 +12668,6 @@ vec4.equals = function (a, b) { module.exports = vec4; -/***/ }), -/* 11 */ -/***/ (function(module, exports) { - -module.exports = function (tag, attrs) { - var el = document.createElementNS('http://www.w3.org/2000/svg', tag); - for (var k in attrs) { - if (attrs.hasOwnProperty(k)) { - el.setAttribute(k, attrs[k]); - } - } - return el; -}; - /***/ }), /* 12 */ /***/ (function(module, exports) { @@ -12862,7 +12862,7 @@ var Oncoprint = (function () { return ctr; } })(); - function Oncoprint(ctr_selector, width) { + function Oncoprint(ctr_selector, width, init_cell_width) { var self = this; this.destroyed = false; @@ -12936,6 +12936,14 @@ var Oncoprint = (function () { .addClass("noselect") .addClass('oncoprintjs__cell_overlay_div'); + var $column_label_canvas = $('') + .attr({'width':'0px', 'height':'0px'}) + .css({'position':'absolute', + 'top':'0px', + 'left':'0px'}) + .addClass("noselect") + .addClass('oncoprintjs__column_label_canvas'); + var $track_info_div = $('
') .css({'position':'absolute'}); @@ -12962,6 +12970,7 @@ var Oncoprint = (function () { $minimap_div.appendTo($ctr); $cell_canvas.appendTo($cell_div); + $column_label_canvas.appendTo($cell_div); $cell_overlay_canvas.appendTo($cell_div); $dummy_scroll_div.appendTo($cell_div); $dummy_scroll_div.on("mousemove mousedown mouseup", function(evt) { @@ -12984,9 +12993,9 @@ var Oncoprint = (function () { this.$cell_canvas = $cell_canvas; this.$cell_overlay_canvas = $cell_overlay_canvas; - this.model = new OncoprintModel(); + this.model = new OncoprintModel({ init_cell_width: init_cell_width }); - this.cell_view = new OncoprintWebGLCellView($cell_div, $cell_canvas, $cell_overlay_canvas, $dummy_scroll_div_contents, this.model, new OncoprintToolTip($tooltip_ctr), function(left, right) { + this.cell_view = new OncoprintWebGLCellView($cell_div, $cell_canvas, $cell_overlay_canvas, $column_label_canvas, $dummy_scroll_div_contents, this.model, new OncoprintToolTip($tooltip_ctr), function(left, right) { var enclosed_ids = self.model.getIdsInLeftInterval(left, right); self.setHorzZoom(self.model.getHorzZoomToFit(self.cell_view.visible_area_width, enclosed_ids)); self.$dummy_scroll_div.scrollLeft(self.model.getZoomedColumnLeft(enclosed_ids[0])); @@ -13146,7 +13155,7 @@ var Oncoprint = (function () { if (oncoprint.model.rendering_suppressed_depth > 0) { return; } - oncoprint.$legend_div.css({'top': oncoprint.model.getCellViewHeight() + 30}); + oncoprint.$legend_div.css({'top': oncoprint.cell_view.getHeight(oncoprint.model) + 30}); }; var setLegendTopAfterTimeout = function (oncoprint) { if (oncoprint.model.rendering_suppressed_depth > 0) { @@ -13159,7 +13168,7 @@ var Oncoprint = (function () { }; var setHeight = function(oncoprint) { - oncoprint.$ctr.css({'min-height': oncoprint.model.getCellViewHeight() + Math.max(oncoprint.$legend_div.outerHeight(), (oncoprint.$minimap_div.is(":visible") ? oncoprint.$minimap_div.outerHeight() : 0)) + 30}); + oncoprint.$ctr.css({'min-height': oncoprint.cell_view.getHeight(oncoprint.model) + Math.max(oncoprint.$legend_div.outerHeight(), (oncoprint.$minimap_div.is(":visible") ? oncoprint.$minimap_div.outerHeight() : 0)) + 30}); }; var resizeAndOrganize = function (oncoprint, onComplete) { @@ -13202,11 +13211,11 @@ var Oncoprint = (function () { }; var maxOncoprintScrollLeft = function(oncoprint) { - return Math.max(0, oncoprint.model.getOncoprintWidth() - oncoprint.cell_view.getWidth()); + return Math.max(0, oncoprint.cell_view.getTotalWidth(oncoprint.model) - oncoprint.cell_view.getWidth()); }; var maxOncoprintScrollTop = function(oncoprint) { - return Math.max(0, oncoprint.model.getOncoprintHeight() - oncoprint.model.getCellViewHeight()); + return Math.max(0, oncoprint.cell_view.getHeight(oncoprint.model) - oncoprint.model.getCellViewHeight()); }; var maxDummyScrollDivScroll = function(oncoprint) { @@ -13221,7 +13230,7 @@ var Oncoprint = (function () { return; } if (visible) { - this.$minimap_div.css({'display': 'block', 'top': this.model.getCellViewHeight() + 30, 'left': $(this.ctr_selector).width() - this.$minimap_div.outerWidth() - 10}); + this.$minimap_div.css({'display': 'block', 'top': this.cell_view.getHeight(this.model) + 30, 'left': $(this.ctr_selector).width() - this.$minimap_div.outerWidth() - 10}); } else { this.$minimap_div.css('display', 'none'); executeMinimapCloseCallbacks(this); @@ -13895,6 +13904,12 @@ var Oncoprint = (function () { resizeAndOrganize(this); } + Oncoprint.prototype.setColumnLabels = function(labels) { + this.model.setColumnLabels(labels); + this.cell_view.setColumnLabels(this.model); + resizeAndOrganizeAfterTimeout(this); + } + Oncoprint.prototype.toSVG = function(with_background) { if(this.webgl_unavailable || this.destroyed) { return; @@ -13917,8 +13932,9 @@ var Oncoprint = (function () { var track_info_group = this.track_info_view.toSVGGroup(this.model, track_info_group_x, 0); everything_group.appendChild(track_info_group); var cell_view_group_x = track_info_group_x + track_info_group.getBBox().width + 10; - everything_group.appendChild(this.cell_view.toSVGGroup(this.model, cell_view_group_x, 0)); - everything_group.appendChild(this.legend_view.toSVGGroup(this.model, 0, label_view_group.getBBox().y + label_view_group.getBBox().height+20)); + var cell_view_group = this.cell_view.toSVGGroup(this.model, cell_view_group_x, 0); + everything_group.appendChild(cell_view_group); + everything_group.appendChild(this.legend_view.toSVGGroup(this.model, 0, cell_view_group.getBBox().y + cell_view_group.getBBox().height+20)); var everything_box = everything_group.getBBox(); var everything_width = everything_box.x + everything_box.width; @@ -14088,9 +14104,9 @@ module.exports = (function setPolyfills() { /***/ (function(module, exports, __webpack_require__) { /* jshint browserify: true, asi: true */ -var binarysearch = __webpack_require__(5); +var binarysearch = __webpack_require__(6); var hasElementsInInterval = __webpack_require__(16); -var CachedProperty = __webpack_require__(6); +var CachedProperty = __webpack_require__(7); var clustering = __webpack_require__(17); var $ = __webpack_require__(0); var BucketSort = __webpack_require__(20); @@ -14196,9 +14212,7 @@ var clamp = function(x, lower, upper) { var OncoprintModel = (function () { var MIN_ZOOM_PIXELS = 100; var MIN_CELL_HEIGHT_PIXELS = 3; - function OncoprintModel(init_cell_padding, init_cell_padding_on, - init_horz_zoom, init_vert_zoom, - init_cell_width, init_track_group_padding) { + function OncoprintModel(params) { var model = this; @@ -14208,21 +14222,22 @@ var OncoprintModel = (function () { // Rendering Properties this.max_height = 500; - this.cell_width = ifndef(init_cell_width, 6); - this.horz_zoom = ifndef(init_horz_zoom, 1); - this.vert_zoom = ifndef(init_vert_zoom, 1); + this.cell_width = ifndef(params.init_cell_width, 6); + this.horz_zoom = ifndef(params.init_horz_zoom, 1); + this.vert_zoom = ifndef(params.init_vert_zoom, 1); this.horz_scroll = 0; this.vert_scroll = 0; this.bottom_padding = 0; - this.track_group_padding = ifndef(init_track_group_padding, 10); - this.cell_padding = ifndef(init_cell_padding, 3); - this.cell_padding_on = ifndef(init_cell_padding_on, true); + this.track_group_padding = ifndef(params.init_track_group_padding, 10); + this.cell_padding = ifndef(params.init_cell_padding, 3); + this.cell_padding_on = ifndef(params.init_cell_padding_on, true); this.cell_padding_off_cell_width_threshold = 2; this.cell_padding_off_because_of_zoom = (this.getCellWidth() < this.cell_padding_off_cell_width_threshold); this.id_order = []; this.hidden_ids = {}; this.track_group_legend_order = []; this.show_track_sublabels = false; + this.column_labels = {}; // Track Properties this.track_important_ids = {}; // a set of "important" ids - only these ids will cause a used rule to become active and thus shown in the legend @@ -15240,9 +15255,14 @@ var OncoprintModel = (function () { return Math.min(this.max_height, this.getOncoprintHeight()); } - OncoprintModel.prototype.getCellViewWidth = function() { - return this.getOncoprintWidth(); + OncoprintModel.prototype.getColumnLabels = function() { + return this.column_labels; + } + + OncoprintModel.prototype.setColumnLabels = function(labels) { + this.column_labels = labels; } + OncoprintModel.prototype.moveTrack = function (track_id, new_previous_track) { function moveContiguousValues(uniqArray, first_value, last_value, new_predecessor) { @@ -33434,10 +33454,11 @@ module.exports = function(module) { /* 24 */ /***/ (function(module, exports, __webpack_require__) { -var gl_matrix = __webpack_require__(7); +var gl_matrix = __webpack_require__(8); var svgfactory = __webpack_require__(2); +var makeSvgElement = __webpack_require__(4); var shapeToVertexes = __webpack_require__(31); -var CachedProperty = __webpack_require__(6); +var CachedProperty = __webpack_require__(7); var Shape = __webpack_require__(12); var $ = __webpack_require__(0); @@ -33466,7 +33487,7 @@ var getNewCanvas = function(view) { var new_canvas = old_canvas.cloneNode(); var parent_node = old_canvas.parentNode; parent_node.removeChild(old_canvas); - parent_node.insertBefore(new_canvas, view.$overlay_canvas[0]); + parent_node.prepend(new_canvas); // keep on bottom since we need overlays to not be hidden view.$canvas = $(new_canvas); view.ctx = null; }; @@ -33522,11 +33543,17 @@ var createShader = function (view, source, type) { return shader; }; +var COLUMN_LABEL_ANGLE = 65; +var COLUMN_LABEL_MARGIN = 30; + var OncoprintWebGLCellView = (function () { - function OncoprintWebGLCellView($container, $canvas, $overlay_canvas, $dummy_scroll_div_contents, model, tooltip, highlight_area_callback, cell_over_callback) { + function OncoprintWebGLCellView($container, $canvas, $overlay_canvas, $column_label_canvas, $dummy_scroll_div_contents, model, tooltip, highlight_area_callback, cell_over_callback) { this.$container = $container; this.$canvas = $canvas; this.$overlay_canvas = $overlay_canvas; + this.$column_label_canvas = $column_label_canvas; + + this.maximum_label_width = 0; this.supersampling_ratio = 2; @@ -33699,6 +33726,10 @@ var OncoprintWebGLCellView = (function () { clearOverlay(view); }; + var getColumnLabelsContext = function(view) { + view.column_label_ctx = view.$column_label_canvas[0].getContext('2d'); + }; + var getWebGLContextAndSetUpMatrices = function(view) { view.ctx = getWebGLCanvasContext(view); (function initializeMatrices(self) { @@ -33784,7 +33815,7 @@ var OncoprintWebGLCellView = (function () { }; var resizeAndClear = function(view, model) { - var height = model.getCellViewHeight(); + var height = view.getHeight(model); var total_width = view.getTotalWidth(model); var visible_area_width = view.visible_area_width; var scrollbar_slack = 20; @@ -33795,15 +33826,20 @@ var OncoprintWebGLCellView = (function () { view.$canvas[0].style.height = height + 'px'; view.$overlay_canvas[0].height = view.supersampling_ratio*height; view.$overlay_canvas[0].style.height = height + 'px'; + view.$column_label_canvas[0].height = view.supersampling_ratio*height; + view.$column_label_canvas[0].style.height = height + 'px'; view.$canvas[0].width = view.supersampling_ratio*visible_area_width; view.$canvas[0].style.width = visible_area_width + 'px'; view.$overlay_canvas[0].width = view.supersampling_ratio*visible_area_width; view.$overlay_canvas[0].style.width = visible_area_width + 'px'; + view.$column_label_canvas[0].width = view.supersampling_ratio*visible_area_width; + view.$column_label_canvas[0].style.width = visible_area_width + 'px'; view.$container.css('height', height); view.$container.css('width', visible_area_width); getWebGLContextAndSetUpMatrices(view); setUpShaders(view); getOverlayContextAndClear(view); + getColumnLabelsContext(view); }; var renderAllTracks = function (view, model, dont_resize) { if (view.rendering_suppressed) { @@ -33824,7 +33860,8 @@ var OncoprintWebGLCellView = (function () { var id_order = model.getIdOrder(); var horz_first_id_in_window_index = arrayFindIndex(id_order, function(id) { return id_to_left[id] >= window_left; }); var horz_first_id_after_window_index = arrayFindIndex(id_order, function(id) { return id_to_left[id] > window_right; }, horz_first_id_in_window_index+1); - var horz_first_id_in_window = (horz_first_id_in_window_index < 1 ? id_order[0] : id_order[horz_first_id_in_window_index - 1]); + horz_first_id_in_window_index = (horz_first_id_in_window_index < 1 ? 0 : horz_first_id_in_window_index - 1); + var horz_first_id_in_window = id_order[horz_first_id_in_window_index]; var horz_first_id_after_window = (horz_first_id_after_window_index === -1 ? null : id_order[horz_first_id_after_window_index]); if (!dont_resize) { @@ -33876,6 +33913,53 @@ var OncoprintWebGLCellView = (function () { view.ctx.drawArrays(view.ctx.TRIANGLES, first_index, first_index_out - first_index); } + + renderColumnLabels(view, model, id_order.slice(horz_first_id_in_window_index, horz_first_id_after_window_index === -1 ? undefined : horz_first_id_after_window_index)); + }; + + var getColumnLabelsFontSize = function(model) { + return model.getCellWidth()/2 + 2; + }; + + var renderColumnLabels = function(view, model, ids) { + // first clear + view.column_label_ctx.fillStyle = "rgba(0,0,0,0)"; + view.column_label_ctx.clearRect(0,0, view.$column_label_canvas[0].width, view.$column_label_canvas[0].height); + view.maximum_label_width = 0; + + // render labels + var labels = model.getColumnLabels(); + + // dont do anything if theres no labels + if (Object.keys(labels).length === 0) { + return; + } + + var y = (model.getOncoprintHeight() + 10)*view.supersampling_ratio; + var x_map = model.getZoomedColumnLeft(); + var scroll_x = view.scroll_x; + var font_size = getColumnLabelsFontSize(model); + var font_family = "Arial"; + var cell_width = model.getCellWidth(); + + view.column_label_ctx.fillStyle = "rgba(0,0,0,1)"; + view.column_label_ctx.font = (view.supersampling_ratio*font_size)+"px "+font_family; + view.column_label_ctx.textAlign = "left"; + for (var i=0; i 0) { + width += this.maximum_label_width*Math.cos(COLUMN_LABEL_ANGLE*Math.PI/180); + } + + return width; } OncoprintWebGLCellView.prototype.getWidth = function() { @@ -34315,6 +34411,19 @@ var OncoprintWebGLCellView = (function () { renderAllTracks(this, model); // in the process it will call resizeAndClear } + OncoprintWebGLCellView.prototype.getHeight = function(model) { + // for now just add fixed height for column labels + // TODO: dont show if zoom is too small + var height = model.getCellViewHeight(); + + if (this.maximum_label_width > 0) { + height += COLUMN_LABEL_MARGIN; + height += this.maximum_label_width*Math.sin(COLUMN_LABEL_ANGLE*Math.PI/180); + } + + return height; + } + OncoprintWebGLCellView.prototype.setCellPaddingOn = function(model) { if (this.rendering_suppressed) { return; @@ -34341,6 +34450,7 @@ var OncoprintWebGLCellView = (function () { var cell_tops = model.getCellTops(); var tracks = model.getTracks(); var zoomedColumnLeft = model.getZoomedColumnLeft(); + // add cell shapes for (var i=0; i -1) { @@ -38920,7 +39054,7 @@ module.exports = function(oncoprint_shape_computed_params, z_index, addVertex) { var svgfactory = __webpack_require__(2); var $ = __webpack_require__(0); -var makeSvgElement = __webpack_require__(11); +var makeSvgElement = __webpack_require__(4); var CIRCLE_X = 25; function circleRadius(view) { @@ -39410,9 +39544,9 @@ module.exports = OncoprintLabelView; */ var Shape = __webpack_require__(12); -var extractRGBA = __webpack_require__(4); +var extractRGBA = __webpack_require__(5); var heatmapColors = __webpack_require__(34); -var binarysearch = __webpack_require__(5); +var binarysearch = __webpack_require__(6); var cloneShallow = __webpack_require__(3).cloneShallow; function ifndef(x, val) { @@ -41933,7 +42067,7 @@ module.exports = OncoprintTrackInfoView; /* 40 */ /***/ (function(module, exports, __webpack_require__) { -var gl_matrix = __webpack_require__(7); +var gl_matrix = __webpack_require__(8); var OncoprintZoomSlider = __webpack_require__(41); var $ = __webpack_require__(0); var zoomToFitIcon = __webpack_require__(42); diff --git a/packages/oncoprintjs/index.d.ts b/packages/oncoprintjs/index.d.ts index c0229eec0c5..8535fad32f4 100644 --- a/packages/oncoprintjs/index.d.ts +++ b/packages/oncoprintjs/index.d.ts @@ -222,8 +222,9 @@ declare module "oncoprintjs" clearMouseOverEffects:()=>void; setTrackMovable:(track_id:TrackId, movable:boolean)=>void; setWidth:(width:number)=>void; + setColumnLabels:(labels:{[uid:string]:string})=>void; - constructor(ctr_selector:string, width:number); + constructor(ctr_selector:string, width:number, init_cell_width:number); destroy:()=>void; } } diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 445c2eda4b3..4633d3f29b3 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -27,7 +27,7 @@ var Oncoprint = (function () { return ctr; } })(); - function Oncoprint(ctr_selector, width) { + function Oncoprint(ctr_selector, width, init_cell_width) { var self = this; this.destroyed = false; @@ -101,6 +101,14 @@ var Oncoprint = (function () { .addClass("noselect") .addClass('oncoprintjs__cell_overlay_div'); + var $column_label_canvas = $('') + .attr({'width':'0px', 'height':'0px'}) + .css({'position':'absolute', + 'top':'0px', + 'left':'0px'}) + .addClass("noselect") + .addClass('oncoprintjs__column_label_canvas'); + var $track_info_div = $('
') .css({'position':'absolute'}); @@ -127,6 +135,7 @@ var Oncoprint = (function () { $minimap_div.appendTo($ctr); $cell_canvas.appendTo($cell_div); + $column_label_canvas.appendTo($cell_div); $cell_overlay_canvas.appendTo($cell_div); $dummy_scroll_div.appendTo($cell_div); $dummy_scroll_div.on("mousemove mousedown mouseup", function(evt) { @@ -149,9 +158,9 @@ var Oncoprint = (function () { this.$cell_canvas = $cell_canvas; this.$cell_overlay_canvas = $cell_overlay_canvas; - this.model = new OncoprintModel(); + this.model = new OncoprintModel({ init_cell_width: init_cell_width }); - this.cell_view = new OncoprintWebGLCellView($cell_div, $cell_canvas, $cell_overlay_canvas, $dummy_scroll_div_contents, this.model, new OncoprintToolTip($tooltip_ctr), function(left, right) { + this.cell_view = new OncoprintWebGLCellView($cell_div, $cell_canvas, $cell_overlay_canvas, $column_label_canvas, $dummy_scroll_div_contents, this.model, new OncoprintToolTip($tooltip_ctr), function(left, right) { var enclosed_ids = self.model.getIdsInLeftInterval(left, right); self.setHorzZoom(self.model.getHorzZoomToFit(self.cell_view.visible_area_width, enclosed_ids)); self.$dummy_scroll_div.scrollLeft(self.model.getZoomedColumnLeft(enclosed_ids[0])); @@ -311,7 +320,7 @@ var Oncoprint = (function () { if (oncoprint.model.rendering_suppressed_depth > 0) { return; } - oncoprint.$legend_div.css({'top': oncoprint.model.getCellViewHeight() + 30}); + oncoprint.$legend_div.css({'top': oncoprint.cell_view.getHeight(oncoprint.model) + 30}); }; var setLegendTopAfterTimeout = function (oncoprint) { if (oncoprint.model.rendering_suppressed_depth > 0) { @@ -324,7 +333,7 @@ var Oncoprint = (function () { }; var setHeight = function(oncoprint) { - oncoprint.$ctr.css({'min-height': oncoprint.model.getCellViewHeight() + Math.max(oncoprint.$legend_div.outerHeight(), (oncoprint.$minimap_div.is(":visible") ? oncoprint.$minimap_div.outerHeight() : 0)) + 30}); + oncoprint.$ctr.css({'min-height': oncoprint.cell_view.getHeight(oncoprint.model) + Math.max(oncoprint.$legend_div.outerHeight(), (oncoprint.$minimap_div.is(":visible") ? oncoprint.$minimap_div.outerHeight() : 0)) + 30}); }; var resizeAndOrganize = function (oncoprint, onComplete) { @@ -367,11 +376,11 @@ var Oncoprint = (function () { }; var maxOncoprintScrollLeft = function(oncoprint) { - return Math.max(0, oncoprint.model.getOncoprintWidth() - oncoprint.cell_view.getWidth()); + return Math.max(0, oncoprint.cell_view.getTotalWidth(oncoprint.model) - oncoprint.cell_view.getWidth()); }; var maxOncoprintScrollTop = function(oncoprint) { - return Math.max(0, oncoprint.model.getOncoprintHeight() - oncoprint.model.getCellViewHeight()); + return Math.max(0, oncoprint.cell_view.getHeight(oncoprint.model) - oncoprint.model.getCellViewHeight()); }; var maxDummyScrollDivScroll = function(oncoprint) { @@ -386,7 +395,7 @@ var Oncoprint = (function () { return; } if (visible) { - this.$minimap_div.css({'display': 'block', 'top': this.model.getCellViewHeight() + 30, 'left': $(this.ctr_selector).width() - this.$minimap_div.outerWidth() - 10}); + this.$minimap_div.css({'display': 'block', 'top': this.cell_view.getHeight(this.model) + 30, 'left': $(this.ctr_selector).width() - this.$minimap_div.outerWidth() - 10}); } else { this.$minimap_div.css('display', 'none'); executeMinimapCloseCallbacks(this); @@ -1060,6 +1069,12 @@ var Oncoprint = (function () { resizeAndOrganize(this); } + Oncoprint.prototype.setColumnLabels = function(labels) { + this.model.setColumnLabels(labels); + this.cell_view.setColumnLabels(this.model); + resizeAndOrganizeAfterTimeout(this); + } + Oncoprint.prototype.toSVG = function(with_background) { if(this.webgl_unavailable || this.destroyed) { return; @@ -1082,8 +1097,9 @@ var Oncoprint = (function () { var track_info_group = this.track_info_view.toSVGGroup(this.model, track_info_group_x, 0); everything_group.appendChild(track_info_group); var cell_view_group_x = track_info_group_x + track_info_group.getBBox().width + 10; - everything_group.appendChild(this.cell_view.toSVGGroup(this.model, cell_view_group_x, 0)); - everything_group.appendChild(this.legend_view.toSVGGroup(this.model, 0, label_view_group.getBBox().y + label_view_group.getBBox().height+20)); + var cell_view_group = this.cell_view.toSVGGroup(this.model, cell_view_group_x, 0); + everything_group.appendChild(cell_view_group); + everything_group.appendChild(this.legend_view.toSVGGroup(this.model, 0, cell_view_group.getBBox().y + cell_view_group.getBBox().height+20)); var everything_box = everything_group.getBBox(); var everything_width = everything_box.x + everything_box.width; diff --git a/packages/oncoprintjs/src/js/oncoprintmodel.js b/packages/oncoprintjs/src/js/oncoprintmodel.js index 50df83b01b8..79d34fae464 100644 --- a/packages/oncoprintjs/src/js/oncoprintmodel.js +++ b/packages/oncoprintjs/src/js/oncoprintmodel.js @@ -107,9 +107,7 @@ var clamp = function(x, lower, upper) { var OncoprintModel = (function () { var MIN_ZOOM_PIXELS = 100; var MIN_CELL_HEIGHT_PIXELS = 3; - function OncoprintModel(init_cell_padding, init_cell_padding_on, - init_horz_zoom, init_vert_zoom, - init_cell_width, init_track_group_padding) { + function OncoprintModel(params) { var model = this; @@ -119,21 +117,22 @@ var OncoprintModel = (function () { // Rendering Properties this.max_height = 500; - this.cell_width = ifndef(init_cell_width, 6); - this.horz_zoom = ifndef(init_horz_zoom, 1); - this.vert_zoom = ifndef(init_vert_zoom, 1); + this.cell_width = ifndef(params.init_cell_width, 6); + this.horz_zoom = ifndef(params.init_horz_zoom, 1); + this.vert_zoom = ifndef(params.init_vert_zoom, 1); this.horz_scroll = 0; this.vert_scroll = 0; this.bottom_padding = 0; - this.track_group_padding = ifndef(init_track_group_padding, 10); - this.cell_padding = ifndef(init_cell_padding, 3); - this.cell_padding_on = ifndef(init_cell_padding_on, true); + this.track_group_padding = ifndef(params.init_track_group_padding, 10); + this.cell_padding = ifndef(params.init_cell_padding, 3); + this.cell_padding_on = ifndef(params.init_cell_padding_on, true); this.cell_padding_off_cell_width_threshold = 2; this.cell_padding_off_because_of_zoom = (this.getCellWidth() < this.cell_padding_off_cell_width_threshold); this.id_order = []; this.hidden_ids = {}; this.track_group_legend_order = []; this.show_track_sublabels = false; + this.column_labels = {}; // Track Properties this.track_important_ids = {}; // a set of "important" ids - only these ids will cause a used rule to become active and thus shown in the legend @@ -1151,9 +1150,14 @@ var OncoprintModel = (function () { return Math.min(this.max_height, this.getOncoprintHeight()); } - OncoprintModel.prototype.getCellViewWidth = function() { - return this.getOncoprintWidth(); + OncoprintModel.prototype.getColumnLabels = function() { + return this.column_labels; } + + OncoprintModel.prototype.setColumnLabels = function(labels) { + this.column_labels = labels; + } + OncoprintModel.prototype.moveTrack = function (track_id, new_previous_track) { function moveContiguousValues(uniqArray, first_value, last_value, new_predecessor) { diff --git a/packages/oncoprintjs/src/js/oncoprintwebglcellview.js b/packages/oncoprintjs/src/js/oncoprintwebglcellview.js index 437e56dc0c3..e120d07b178 100644 --- a/packages/oncoprintjs/src/js/oncoprintwebglcellview.js +++ b/packages/oncoprintjs/src/js/oncoprintwebglcellview.js @@ -1,5 +1,6 @@ var gl_matrix = require('gl-matrix'); var svgfactory = require('./svgfactory.js'); +var makeSvgElement = require('./makesvgelement.js'); var shapeToVertexes = require('./oncoprintshapetovertexes.js'); var CachedProperty = require('./CachedProperty.js'); var Shape = require('./oncoprintshape.js'); @@ -30,7 +31,7 @@ var getNewCanvas = function(view) { var new_canvas = old_canvas.cloneNode(); var parent_node = old_canvas.parentNode; parent_node.removeChild(old_canvas); - parent_node.insertBefore(new_canvas, view.$overlay_canvas[0]); + parent_node.prepend(new_canvas); // keep on bottom since we need overlays to not be hidden view.$canvas = $(new_canvas); view.ctx = null; }; @@ -86,11 +87,17 @@ var createShader = function (view, source, type) { return shader; }; +var COLUMN_LABEL_ANGLE = 65; +var COLUMN_LABEL_MARGIN = 30; + var OncoprintWebGLCellView = (function () { - function OncoprintWebGLCellView($container, $canvas, $overlay_canvas, $dummy_scroll_div_contents, model, tooltip, highlight_area_callback, cell_over_callback) { + function OncoprintWebGLCellView($container, $canvas, $overlay_canvas, $column_label_canvas, $dummy_scroll_div_contents, model, tooltip, highlight_area_callback, cell_over_callback) { this.$container = $container; this.$canvas = $canvas; this.$overlay_canvas = $overlay_canvas; + this.$column_label_canvas = $column_label_canvas; + + this.maximum_label_width = 0; this.supersampling_ratio = 2; @@ -263,6 +270,10 @@ var OncoprintWebGLCellView = (function () { clearOverlay(view); }; + var getColumnLabelsContext = function(view) { + view.column_label_ctx = view.$column_label_canvas[0].getContext('2d'); + }; + var getWebGLContextAndSetUpMatrices = function(view) { view.ctx = getWebGLCanvasContext(view); (function initializeMatrices(self) { @@ -348,7 +359,7 @@ var OncoprintWebGLCellView = (function () { }; var resizeAndClear = function(view, model) { - var height = model.getCellViewHeight(); + var height = view.getHeight(model); var total_width = view.getTotalWidth(model); var visible_area_width = view.visible_area_width; var scrollbar_slack = 20; @@ -359,15 +370,20 @@ var OncoprintWebGLCellView = (function () { view.$canvas[0].style.height = height + 'px'; view.$overlay_canvas[0].height = view.supersampling_ratio*height; view.$overlay_canvas[0].style.height = height + 'px'; + view.$column_label_canvas[0].height = view.supersampling_ratio*height; + view.$column_label_canvas[0].style.height = height + 'px'; view.$canvas[0].width = view.supersampling_ratio*visible_area_width; view.$canvas[0].style.width = visible_area_width + 'px'; view.$overlay_canvas[0].width = view.supersampling_ratio*visible_area_width; view.$overlay_canvas[0].style.width = visible_area_width + 'px'; + view.$column_label_canvas[0].width = view.supersampling_ratio*visible_area_width; + view.$column_label_canvas[0].style.width = visible_area_width + 'px'; view.$container.css('height', height); view.$container.css('width', visible_area_width); getWebGLContextAndSetUpMatrices(view); setUpShaders(view); getOverlayContextAndClear(view); + getColumnLabelsContext(view); }; var renderAllTracks = function (view, model, dont_resize) { if (view.rendering_suppressed) { @@ -388,7 +404,8 @@ var OncoprintWebGLCellView = (function () { var id_order = model.getIdOrder(); var horz_first_id_in_window_index = arrayFindIndex(id_order, function(id) { return id_to_left[id] >= window_left; }); var horz_first_id_after_window_index = arrayFindIndex(id_order, function(id) { return id_to_left[id] > window_right; }, horz_first_id_in_window_index+1); - var horz_first_id_in_window = (horz_first_id_in_window_index < 1 ? id_order[0] : id_order[horz_first_id_in_window_index - 1]); + horz_first_id_in_window_index = (horz_first_id_in_window_index < 1 ? 0 : horz_first_id_in_window_index - 1); + var horz_first_id_in_window = id_order[horz_first_id_in_window_index]; var horz_first_id_after_window = (horz_first_id_after_window_index === -1 ? null : id_order[horz_first_id_after_window_index]); if (!dont_resize) { @@ -440,6 +457,53 @@ var OncoprintWebGLCellView = (function () { view.ctx.drawArrays(view.ctx.TRIANGLES, first_index, first_index_out - first_index); } + + renderColumnLabels(view, model, id_order.slice(horz_first_id_in_window_index, horz_first_id_after_window_index === -1 ? undefined : horz_first_id_after_window_index)); + }; + + var getColumnLabelsFontSize = function(model) { + return model.getCellWidth()/2 + 2; + }; + + var renderColumnLabels = function(view, model, ids) { + // first clear + view.column_label_ctx.fillStyle = "rgba(0,0,0,0)"; + view.column_label_ctx.clearRect(0,0, view.$column_label_canvas[0].width, view.$column_label_canvas[0].height); + view.maximum_label_width = 0; + + // render labels + var labels = model.getColumnLabels(); + + // dont do anything if theres no labels + if (Object.keys(labels).length === 0) { + return; + } + + var y = (model.getOncoprintHeight() + 10)*view.supersampling_ratio; + var x_map = model.getZoomedColumnLeft(); + var scroll_x = view.scroll_x; + var font_size = getColumnLabelsFontSize(model); + var font_family = "Arial"; + var cell_width = model.getCellWidth(); + + view.column_label_ctx.fillStyle = "rgba(0,0,0,1)"; + view.column_label_ctx.font = (view.supersampling_ratio*font_size)+"px "+font_family; + view.column_label_ctx.textAlign = "left"; + for (var i=0; i 0) { + width += this.maximum_label_width*Math.cos(COLUMN_LABEL_ANGLE*Math.PI/180); + } + + return width; } OncoprintWebGLCellView.prototype.getWidth = function() { @@ -879,6 +955,19 @@ var OncoprintWebGLCellView = (function () { renderAllTracks(this, model); // in the process it will call resizeAndClear } + OncoprintWebGLCellView.prototype.getHeight = function(model) { + // for now just add fixed height for column labels + // TODO: dont show if zoom is too small + var height = model.getCellViewHeight(); + + if (this.maximum_label_width > 0) { + height += COLUMN_LABEL_MARGIN; + height += this.maximum_label_width*Math.sin(COLUMN_LABEL_ANGLE*Math.PI/180); + } + + return height; + } + OncoprintWebGLCellView.prototype.setCellPaddingOn = function(model) { if (this.rendering_suppressed) { return; @@ -905,6 +994,7 @@ var OncoprintWebGLCellView = (function () { var cell_tops = model.getCellTops(); var tracks = model.getTracks(); var zoomedColumnLeft = model.getZoomedColumnLeft(); + // add cell shapes for (var i=0; i