From 195de669836ef03b79d08406ae2ef6b1c5b782f5 Mon Sep 17 00:00:00 2001 From: jrobinso <933148+jrobinso@users.noreply.github.com> Date: Fri, 11 Oct 2024 22:51:03 -0700 Subject: [PATCH] shoebox updates --- js/shoebox/shoeboxColorScale.js | 88 +++++++++++++++++ js/{feature => shoebox}/shoeboxTrack.js | 120 ++++++++++-------------- js/trackFactory.js | 2 +- js/ui/menuUtils.js | 2 +- 4 files changed, 137 insertions(+), 75 deletions(-) create mode 100644 js/shoebox/shoeboxColorScale.js rename js/{feature => shoebox}/shoeboxTrack.js (62%) diff --git a/js/shoebox/shoeboxColorScale.js b/js/shoebox/shoeboxColorScale.js new file mode 100644 index 000000000..fb4d2fa58 --- /dev/null +++ b/js/shoebox/shoeboxColorScale.js @@ -0,0 +1,88 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2017 The Regents of the University of California + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +import {IGVMath} from "../../node_modules/igv-utils/src/index.js" + +const defaultColorScaleConfig = {min: 0, max: 3000, color: "rgb(0,0,255)"} + +class ShoeboxColorScale { + + constructor(scale) { + + scale = scale || defaultColorScaleConfig + this.max = scale.max + this.min = scale.min || 0 + this.cache = [] + this.nbins = 1000 + this.binsize = (this.max - this.min) / this.nbins + this.updateColor(scale.color || "rgb(0,0,255)") + + } + updateColor(color) { + const comps = color.substring(4).replace(")", "").split(",") + if (comps.length === 3) { + this.r = Number.parseInt(comps[0].trim()) + this.g = Number.parseInt(comps[1].trim()) + this.b = Number.parseInt(comps[2].trim()) + } + } + + setMinMax(min, max) { + this.min = min + this.max = max + this.cache = [] + this.binsize = (this.max - this.min) / this.nbins + } + + getColor(value) { + const low = 0 + if (value < this.min) return "white" + + const bin = Math.floor((Math.min(this.max, value) - this.min) / this.binsize) + if (undefined === this.cache[bin]) { + const alpha = (IGVMath.clamp(value, low, this.max) - low) / (this.max - low) + this.cache[bin] = `rgba(${this.r},${this.g},${this.b}, ${alpha})` + } + return this.cache[bin] + } + + stringify() { + return "" + this.min + "," + this.max + ',' + `rgb(${this.r},${this.g},${this.b})` + } + + static parse(string) { + + const tokens = str.split(",") + + const cs = { + min: Number.parseFloat(tokens[0]), + max: Number.parseFloat(tokens[1]), + color: tokens[2] + } + return new ShoeboxColorScale(cs) + } + +} + + +export default ShoeboxColorScale diff --git a/js/feature/shoeboxTrack.js b/js/shoebox/shoeboxTrack.js similarity index 62% rename from js/feature/shoeboxTrack.js rename to js/shoebox/shoeboxTrack.js index 5aa620a25..888db6589 100755 --- a/js/feature/shoeboxTrack.js +++ b/js/shoebox/shoeboxTrack.js @@ -1,15 +1,8 @@ import $ from "../vendor/jquery-3.3.1.slim.js" -import FeatureSource from './featureSource.js' +import FeatureSource from "../feature/featureSource.js" import TrackBase from "../trackBase.js" import IGVGraphics from "../igv-canvas.js" -import {IGVMath} from "../../node_modules/igv-utils/src/index.js" -import {createCheckbox} from "../igv-icons.js" -import {GradientColorScale} from "../util/colorScale.js" -import {ColorTable} from "../util/colorPalletes.js" -import SampleInfo from "../sample/sampleInfo.js" -import HicColorScale from "../hic/hicColorScale.js" -import ShoeboxSource from "../hic/shoeboxSource.js" -import {doSortByAttributes} from "../sample/sampleUtils.js" +import ShoeboxColorScale from "./shoeboxColorScale.js" class ShoeboxTrack extends TrackBase { @@ -18,7 +11,6 @@ class ShoeboxTrack extends TrackBase { height: 300, rowHeight: 3, max: 3000, - color: "rgb(0,0,255)" } constructor(config, browser) { @@ -57,49 +49,58 @@ class ShoeboxTrack extends TrackBase { // Must do the following after setting track properties as they can be overriden via a track line // Color settings - const max = this.dataRange.max - let r = 0; - let g = 0; - let b = 0; - if(this.color && this.color.startsWith("rgb(")) { - const comps = this.color.substring(4).replace(")","").split(",") - if(comps.length === 3) { - r = Number.parseInt(comps[0].trim()) - g = Number.parseInt(comps[1].trim()) - b = Number.parseInt(comps[2].trim()) - } + if (this.config.colorScale) { + this.colorScale = ShoeboxColorScale.parse(this.config.colorScale) + } else { + const min = this.dataRange.min + const max = this.dataRange.max + this.colorScale = new ShoeboxColorScale({min, max, color: this.color}) } - this.colorScale = new HicColorScale({threshold: max, r,g,b}) + } + get color() { + return this._color || "rgb(0,0,255)" } + set color(color) { + this._color = color + if (this.colorScale) { + this.colorScale.updateColor(color) + } + } menuItemList() { const menuItems = [] - if (this.colorScale) { - menuItems.push('
') - - function dialogPresentationHandler(e) { - this.browser.inputDialog.present({ - label: 'Color Scale Threshold', - value: this.colorScale.threshold, - callback: () => { - const t = Number(this.browser.inputDialog.value, 10) - if (t) { - this.colorScale.setThreshold(t) - this.trackView.repaintViews() - } - } - }, e) - } - menuItems.push({object: $('
Set color scale threshold
'), dialog: dialogPresentationHandler}) + menuItems.push('
') + + // Data range + let object = $('
') + object.text('Set data range') + + function dialogPresentationHandler() { + + if (this.trackView.track.selected) { + this.browser.dataRangeDialog.configure(this.trackView.browser.getSelectedTrackViews()) + } else { + this.browser.dataRangeDialog.configure(this.trackView) + } + this.browser.dataRangeDialog.present($(this.browser.columnContainer)) } + menuItems.push({object, dialog: dialogPresentationHandler}) + return menuItems + } + setDataRange({min, max}) { + this.dataRange.min = min + this.dataRange.max = max + this.colorScale.min = min + this.colorScale.max = max + this.trackView.repaintViews() } hasSamples() { @@ -231,43 +232,16 @@ class ShoeboxTrack extends TrackBase { return items } - contextMenuItemList(clickState) { + get supportsWholeGenome() { + return false + } - const genomicLocation = clickState.genomicLocation + getState() { - const sortHandler = (sort) => { - const viewport = clickState.viewport - const features = viewport.cachedFeatures - this.sortByValue(sort, features) - } + const config = super.getState() + config.colorScale = this.colorScale.stringify() + return config - // We can't know genomic location intended with precision, define a buffer 5 "pixels" wide in genomic coordinates - const bpWidth = clickState.referenceFrame.toBP(2.5) - - return ["DESC", "ASC"].map(direction => { - const dirLabel = direction === "DESC" ? "descending" : "ascending" - const sortLabel = this.type === 'seg' || this.type === 'shoebox' ? - `Sort by value (${dirLabel})` : - `Sort by type (${dirLabel})` - return { - label: sortLabel, - click: () => { - const sort = { - option: "VALUE", // Either VALUE or ATTRIBUTE - direction, - chr: clickState.referenceFrame.chr, - start: Math.floor(genomicLocation - bpWidth), - end: Math.floor(genomicLocation + bpWidth) - } - sortHandler(sort) - this.config.sort = sort - } - } - }) - } - - get supportsWholeGenome() { - return false } } diff --git a/js/trackFactory.js b/js/trackFactory.js index 86146e043..4aa38d91a 100644 --- a/js/trackFactory.js +++ b/js/trackFactory.js @@ -14,7 +14,7 @@ import IdeogramTrack from "./ideogramTrack.js" import SpliceJunctionTrack from "./feature/spliceJunctionTrack.js" import BlatTrack from "./blat/blatTrack.js" import CNVPytorTrack from "./cnvpytor/cnvpytorTrack.js" -import ShoeboxTrack from "./feature/shoeboxTrack.js" +import ShoeboxTrack from "./shoebox/shoeboxTrack.js" //import CNVPytorTrack from "./CNVpytor/cnvpytorTrack.js" diff --git a/js/ui/menuUtils.js b/js/ui/menuUtils.js index 98feb1cb3..c7db4c4d6 100644 --- a/js/ui/menuUtils.js +++ b/js/ui/menuUtils.js @@ -4,7 +4,7 @@ import Dialog from "./components/dialog.js" import $ from "../vendor/jquery-3.3.1.slim.js" import {colorPalettes} from "../util/colorPalletes.js" -const colorPickerTrackTypeSet = new Set(['bedtype', 'alignment', 'annotation', 'variant', 'wig', 'interact']) +const colorPickerTrackTypeSet = new Set(['bedtype', 'alignment', 'annotation', 'variant', 'wig', 'interact', 'shoebox']) const vizWindowTypes = new Set(['alignment', 'annotation', 'variant', 'eqtl', 'qtl', 'snp', 'shoebox', 'wig'])