Loading app/package-lock.json +1640 −256 File changed.Preview size limit exceeded, changes collapsed. Show changes app/package.json +1 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ "react-sigma-v2": "^1.3.0", "react-tooltip": "^4.2.21", "react-use": "^17.3.1", "reaviz": "^13.1.8", "rooks": "^5.7.3", "sigma": "^2.2.0", "simple-statistics": "^7.7.0", Loading app/src/components/RightPanel/ChromatinViewportConfigurationPanel.tsx +3 −3 Original line number Diff line number Diff line Loading @@ -274,7 +274,7 @@ export function ChromatinViewportConfigurationPanel(props: { const high = quantiles[1] + 1.5 * iqr; const radius = low; const radiusRange = { min: low, max: high }; const radiusRange = { min: 0.0, max: high }; //#region Calculate radius range newData.push({ Loading Loading @@ -742,8 +742,8 @@ export function ChromatinViewportConfigurationPanel(props: { /> <Slider label="Probe Size" min={0} max={1} min={0.01} max={1.0} step={0.01} value={configuration.sasa.probeSize} showValue={true} Loading app/src/components/viewports/ChromatinViewport.tsx +30 −228 Original line number Diff line number Diff line Loading @@ -226,19 +226,33 @@ export function ChromatinViewport(props: { return; } const mapScaleToChromatin = (chromatinPart: GraphicsModule.ChromatinPart, values: Array<number>, scale: chroma.Scale): Array<vec4> => { const ratio = Math.max(...values); const valuesNormalized = values.map(v => v / ratio); const colors: Array<vec4> = valuesNormalized.map(v => { return scale(v).gl(); }); return colors; } const newColors = new Array(configuration.data.length); for (const [configurationDatumIndex, configurationDatum] of configuration.data.entries()) { const data3D = viewport.getChromatinPartByDataId(configurationDatumIndex); const datum = data.data.find(d => d.id === configurationDatum.id)?.values as Positions3D; const chromatinPart = viewport.getChromatinPartByDataId(configurationDatumIndex); let dataMarkers = null; if (!data3D) { if (!datum || !chromatinPart) { continue; } const binsAmount = data3D.getBinsPositions().length; const positions = datum.positions; const binsAmount = chromatinPart.getBinsPositions().length; if (configurationDatum.colorMappingMode == "single-color") { newColors[configurationDatumIndex] = data3D.cacheColorArray(new Array(binsAmount).fill(vec4.fromValues( newColors[configurationDatumIndex] = chromatinPart.cacheColorArray(new Array(binsAmount).fill(vec4.fromValues( configurationDatum.color.r / 255.0, configurationDatum.color.g / 255.0, configurationDatum.color.b / 255.0, Loading Loading @@ -269,239 +283,27 @@ export function ChromatinViewport(props: { for (let i = 0; i < binsAmount; i++) { finalColors[i] = colors[finalColorIndices[i]]; } newColors[configurationDatumIndex] = data3D.cacheColorArray(finalColors); } } /* const mapScaleToChromatin = (values: Array<number>, scale: chroma.Scale): Array<Array<vec4>> => { const ratio = Math.max(...values); const valuesNormalized = values.map(v => v / ratio); const allColors: Array<Array<vec4>> = new Array(data3D.chromosomes.length); for (let chromosomeIndex = 0; chromosomeIndex < configuration.chromosomes.length; chromosomeIndex++) { const chromatinPart = viewport.getChromatinPartByChromosomeIndex(chromosomeIndex); if (!chromatinPart) { continue; } const chromosomeBinOffset = chromatineSlices[chromosomeIndex].from; const colors: Array<vec4> = valuesNormalized.slice(chromosomeBinOffset, chromosomeBinOffset + chromatinPart.getBinsPositions().length).map(v => { return scale(v).gl(); }); allColors[chromosomeIndex] = chromatinPart.cacheColorArray(colors); } return allColors; } if (configuration.colorMappingMode == '1d-density') { const data1d: Array<{ chromosome: string, from: number, to: number }> | null = data.data.find(d => d.id == isoDataID.wrap(configuration.mapValues.id))?.values as Sparse1DTextData | Sparse1DNumericData | null; if (!data1d) { return; } const scale = chroma.scale(['white', 'blue']); const countPerBin: Array<number> = Array(data3D.values.length); _.fill(countPerBin, 0) for (let chromosomeIndex = 0; chromosomeIndex < configuration.chromosomes.length; chromosomeIndex++) { const partInfo = chromatineSlices[chromosomeIndex]; const chromosomeData1d = data1d.filter(d => d.chromosome == partInfo.name); const res = data3D.basePairsResolution; for (let binIndex = 0; binIndex < partInfo.to - partInfo.from; binIndex++) { for (const datum of chromosomeData1d) { if (datum.from <= (binIndex + 1) * res && datum.to >= binIndex * res) { countPerBin[binIndex + partInfo.from] += 1; } } } } const logCountPerBin = countPerBin.map(v => Math.log(v) + 1); setInnerColors(() => mapScaleToChromatin(logCountPerBin, scale)); } if (configuration.colorMappingMode == '1d-numerical') { const data1d: Sparse1DNumericData | null = data.data.find(d => d.id == isoDataID.wrap(configuration.mapValues.id))?.values as Sparse1DNumericData | null; if (!data1d) { return; } const scale = chroma.scale(['white', 'blue']); const valuesPerBin: Array<Array<number>> = Array.from(Array(data3D.values.length), () => []) for (let chromosomeIndex = 0; chromosomeIndex < configuration.chromosomes.length; chromosomeIndex++) { const partInfo = chromatineSlices[chromosomeIndex]; const chromosomeData1d = data1d.filter(d => d.chromosome == partInfo.name); const res = data3D.basePairsResolution; for (let binIndex = 0; binIndex < partInfo.to - partInfo.from; binIndex++) { for (const datum of chromosomeData1d) { if (datum.from <= (binIndex + 1) * res && datum.to >= binIndex * res) { valuesPerBin[binIndex + partInfo.from].push(datum.value); } } } } const aggregationFunction: (n: Array<number>) => number | undefined = { "min": _.min, "max": _.max, "mean": _.mean, "median": median, "sum": _.sum }[configuration.mapValues.aggregationFunction] const aggregatedValuesPerBin = valuesPerBin.map((vs) => { const result = aggregationFunction(vs) if (result == null || isNaN(result)) { return 0; } return result; }) setInnerColors(() => mapScaleToChromatin(aggregatedValuesPerBin, scale)); } if (configuration.colorMappingMode == "centromers") { const mapData1D: Positions3D | null = data.data.find(d => d.id == isoDataID.wrap(configuration.mapValues.id))?.values as Positions3D | null; if (!mapData1D) { return; } const centromereBins = new Array(mapData1D.length); // Normalize centromeres to current bounding box const normalizeCenter = data3D.normalizeCenter; const normalizeScale = data3D.normalizeScale; const centromeres: Array<vec3> = []; for (const c of mapData1D) { let centromere = vec3.fromValues(c.x, c.y, c.z); centromere = vec3.sub(vec3.create(), centromere, normalizeCenter); centromere = vec3.scale(vec3.create(), centromere, normalizeScale); centromeres.push(centromere); } // Map centromere 3D position to 1D bin index for (let centromereIndex = 0; centromereIndex < centromeres.length; centromereIndex++) { let minDistance = 1.0; let minIndex = -1; for (let valueIndex = 0; valueIndex < data3D.values.length; valueIndex++) { const value = vec3.fromValues(data3D.values[valueIndex].x, data3D.values[valueIndex].y, data3D.values[valueIndex].z); const diff = vec3.sub(vec3.create(), centromeres[centromereIndex], value); const distance = vec3.dot(diff, diff); if (distance < minDistance) { minDistance = distance; minIndex = valueIndex; } } centromereBins[centromereIndex] = minIndex; } // Map bin to distance const distances: Array<number> = []; for (let valueIndex = 0; valueIndex < data3D.values.length; valueIndex++) { const distance = Math.min(...centromereBins.map((v) => Math.abs(v - valueIndex))); distances.push(distance); } // Color inside with mapping // const colorScale = chroma.scale('YlGnBu'); const colorScale = chroma.scale(['white', 'blue']); setInnerColors(() => mapScaleToChromatin(distances, colorScale)); } if (configuration.colorMappingMode == "linear-order") { const order: Array<number> = []; for (let chromosomeIndex = 0; chromosomeIndex < configuration.chromosomes.length; chromosomeIndex++) { const partInfo = chromatineSlices[chromosomeIndex]; for (let o = 0; o < partInfo.to - partInfo.from + 1; o++) { order.push(o); } } const colorScale = chroma.scale('YlGnBu'); //pick better color scale setInnerColors(() => mapScaleToChromatin(order, colorScale)); } if (configuration.colorMappingMode == 'sasa') { newColors[configurationDatumIndex] = chromatinPart.cacheColorArray(finalColors); } else if (configurationDatum.colorMappingMode == "sasa") { if (configuration.sasa.method == 'generated') { throw "Not implemented" throw "Not implemented"; } //TODO: per chromosome or whole chromosome const globalSasaValues: Array<number> = []; if (!configuration.sasa.individual) { globalSasaValues.push(...sasa(data3D.values, { method: configuration.sasa.method, probe_size: configuration.sasa.probeSize, }, configuration.sasa.accuracy)) } else { for (let chromosomeIndex = 0; chromosomeIndex < configuration.chromosomes.length; chromosomeIndex++) { const partInfo = chromatineSlices[chromosomeIndex]; const chromosomePositions = data3D.values.slice(partInfo.from, partInfo.to + 1); globalSasaValues.push(...sasa(chromosomePositions, { //TODO: per chromosome or whole chromosome const globalSasaValues = sasa(positions, { method: configuration.sasa.method, probe_size: configuration.sasa.probeSize, }, configuration.sasa.accuracy)); } }, configuration.sasa.accuracy); //TODO: fix underlying bug where the something sometimes don't contain last bin of the chromosome if (globalSasaValues.length < data3D.values.length) { globalSasaValues.push(globalSasaValues.reduce((a, b) => a + b, 0) / globalSasaValues.length); } } const colorScale = chroma.scale(['white', 'green']); setInnerColors(() => mapScaleToChromatin(globalSasaValues, colorScale)); } if (configuration.colorMappingMode == '3d-density') { //TODO: per chromosome or whole chromosome const densities: Array<number> = []; if (!configuration.density.individual) { densities.push(...density(data3D.values, configuration.density.probeSize)) } else { // if (globalSasaValues.length < chromatinPart.values.length) { // globalSasaValues.push(globalSasaValues.reduce((a, b) => a + b, 0) / globalSasaValues.length); // } for (let chromosomeIndex = 0; chromosomeIndex < configuration.chromosomes.length; chromosomeIndex++) { const partInfo = chromatineSlices[chromosomeIndex]; const chromosomePositions = data3D.values.slice(partInfo.from, partInfo.to + 1); densities.push(...density( chromosomePositions, configuration.density.probeSize // TODO: user setting )); } const colorScale = chroma.scale(['#fcfdfd', '#010a4e']); newColors[configurationDatumIndex] = chromatinPart.cacheColorArray(mapScaleToChromatin(chromatinPart, globalSasaValues, colorScale)); } //TODO: fix underlying bug where the data3d.values sometimes don't contain last bin of the chromosome if (densities.length < data3D.values.length) { console.warn("Fixing bullshit") console.log(densities) console.log(data3D.values) densities.push(densities.reduce((a, b) => a + b, 0) / densities.length); } const colorScale = chroma.scale(['white', 'red']); setInnerColors(() => mapScaleToChromatin(densities, colorScale)); } */ setColors(() => newColors); }, [viewport, globalSelections.selections, configuration.data, configuration.sasa, data.data, configuration.chromosomes, configuration.density]); Loading app/src/components/viewports/TADViewport.tsx +60 −4 Original line number Diff line number Diff line Loading @@ -9,7 +9,8 @@ import { BinPosition, squareDiameter } from "../../modules/graphics"; import { SelectionAction, SelectionActionKind, SelectionState } from "../../modules/storage/models/selections"; import { DataAction, DataState, Positions3D } from "../../modules/storage/models/data"; import { useConfiguration, useSelections } from "../hooks"; import { sasa } from "../../modules/sasa"; import { ChartShallowDataShape, LineChart, LineSeries, ScatterPlot, SparklineChart } from 'reaviz'; function groupSubsequentNumbers(array: Array<number>): Array<Array<number>> { return array.reduce<Array<Array<number>>>((grouped, next) => { Loading Loading @@ -72,6 +73,8 @@ export function TADViewport(props: { const [binGroups, setBinGroups] = useState<Array<Array<number>>>([]); const [svgNumbers, setSvgNumbers] = useState<Array<JSX.Element>>([]); const [sasaValues, setSasaValues] = useState<Array<Array<number>>>([]); const updatePositions = () => { if (!viewport || !viewport.canvas) { return; Loading Loading @@ -111,7 +114,39 @@ export function TADViewport(props: { positions.push(vec4.fromValues(values[i].x, values[i].y, values[i].z, 1.0)); } const globalSasaValues = [sasa(values, { method: 'constant', probe_size: 0.02, }, 100)]; viewport.setPositions(positions); for(let lod = 1; lod < 32; lod++) { const size = viewport.globals.sizes[lod]; if (size === 0) { break; } const offset = viewport.globals.offsets[lod-1]; const values = []; for(let i = 0; i < size; i++) { const j = i; if (i == size - 1 && viewport.globals.sizes[lod - 1] % 2 !== 0) { values.push((globalSasaValues[lod-1][j] + globalSasaValues[lod-1][j + 1] + globalSasaValues[lod-1][j + 2]) * 0.33); break; } else { values.push((globalSasaValues[lod-1][j] + globalSasaValues[lod-1][j + 1]) * 0.5); } } globalSasaValues.push(values); console.log(globalSasaValues); } setSasaValues(() => globalSasaValues); break; } } Loading Loading @@ -574,6 +609,15 @@ export function TADViewport(props: { setSelectionsRanges(() => selectionsRanges); }, [viewport, data, allSelections, selections, viewport.currentLoD]); let maxSasa = 0; let normalizedSasaValues = null; let xSize = 0; if (sasaValues[viewport.currentLoD] && tracksBlock) { maxSasa = Math.max(...sasaValues[viewport.currentLoD]); normalizedSasaValues = sasaValues[viewport.currentLoD].map(v => (v / maxSasa)); xSize = tracksBlock.width / normalizedSasaValues.length; } return (<div style={{ width: '100%', height: '100%', overflow: 'hidden', position: 'relative' }}> <canvas ref={canvasElement} style={{ width: '100%', height: '100%', overflow: 'hidden', position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }} onClick={onClick}></canvas> <svg style={{ width: '100%', height: '100%', overflow: 'hidden', position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, pointerEvents: 'none' }}> Loading @@ -589,7 +633,7 @@ export function TADViewport(props: { )} {/* {svgNumbers} */} </svg> {(currentBinsAmount && tracksBlock && !isNaN(tracksBlock.top)) && (<div className={'topDiv'}> {(currentBinsAmount && tracksBlock && normalizedSasaValues && !isNaN(tracksBlock.top)) && (<div className={'topDiv'}> <div style={{ width: tracksBlock.width, position: 'absolute', Loading @@ -604,7 +648,8 @@ export function TADViewport(props: { height: '8px', display: 'grid', marginTop: '8px', gridTemplateColumns: 'repeat(' + currentBinsAmount + ', 1fr)' gridTemplateColumns: 'repeat(' + currentBinsAmount + ', 1fr)', position: 'absolute' }}> {selectionRange.map((range, index) => { return <div key={index} style={{ Loading @@ -615,6 +660,17 @@ export function TADViewport(props: { </div> } else { return <div key={selectionRangeIndex}></div> } })} <div style={{width: '100%', height: '40px' }}></div> <SparklineChart width={tracksBlock.width} height={80} data={ normalizedSasaValues.map((v, i) => { return { key: i, data: 1.0 - v } as ChartShallowDataShape } ) }> </SparklineChart> {/* <svg style={{marginTop: '40px'}} width={tracksBlock.width.toString()} height={"80"} viewBox={"0 0 " + tracksBlock.width.toString() + " 80"}> <path fill="none" stroke="blue" strokeWidth={"1"} d={"" + normalizedSasaValues.map((v, i) => { return (i == 0 ? "M" : " L") + (i * xSize).toString() + "," + (80 - v).toString(); })} /> </svg> */} </div> </div>)} </div> Loading Loading
app/package-lock.json +1640 −256 File changed.Preview size limit exceeded, changes collapsed. Show changes
app/package.json +1 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ "react-sigma-v2": "^1.3.0", "react-tooltip": "^4.2.21", "react-use": "^17.3.1", "reaviz": "^13.1.8", "rooks": "^5.7.3", "sigma": "^2.2.0", "simple-statistics": "^7.7.0", Loading
app/src/components/RightPanel/ChromatinViewportConfigurationPanel.tsx +3 −3 Original line number Diff line number Diff line Loading @@ -274,7 +274,7 @@ export function ChromatinViewportConfigurationPanel(props: { const high = quantiles[1] + 1.5 * iqr; const radius = low; const radiusRange = { min: low, max: high }; const radiusRange = { min: 0.0, max: high }; //#region Calculate radius range newData.push({ Loading Loading @@ -742,8 +742,8 @@ export function ChromatinViewportConfigurationPanel(props: { /> <Slider label="Probe Size" min={0} max={1} min={0.01} max={1.0} step={0.01} value={configuration.sasa.probeSize} showValue={true} Loading
app/src/components/viewports/ChromatinViewport.tsx +30 −228 Original line number Diff line number Diff line Loading @@ -226,19 +226,33 @@ export function ChromatinViewport(props: { return; } const mapScaleToChromatin = (chromatinPart: GraphicsModule.ChromatinPart, values: Array<number>, scale: chroma.Scale): Array<vec4> => { const ratio = Math.max(...values); const valuesNormalized = values.map(v => v / ratio); const colors: Array<vec4> = valuesNormalized.map(v => { return scale(v).gl(); }); return colors; } const newColors = new Array(configuration.data.length); for (const [configurationDatumIndex, configurationDatum] of configuration.data.entries()) { const data3D = viewport.getChromatinPartByDataId(configurationDatumIndex); const datum = data.data.find(d => d.id === configurationDatum.id)?.values as Positions3D; const chromatinPart = viewport.getChromatinPartByDataId(configurationDatumIndex); let dataMarkers = null; if (!data3D) { if (!datum || !chromatinPart) { continue; } const binsAmount = data3D.getBinsPositions().length; const positions = datum.positions; const binsAmount = chromatinPart.getBinsPositions().length; if (configurationDatum.colorMappingMode == "single-color") { newColors[configurationDatumIndex] = data3D.cacheColorArray(new Array(binsAmount).fill(vec4.fromValues( newColors[configurationDatumIndex] = chromatinPart.cacheColorArray(new Array(binsAmount).fill(vec4.fromValues( configurationDatum.color.r / 255.0, configurationDatum.color.g / 255.0, configurationDatum.color.b / 255.0, Loading Loading @@ -269,239 +283,27 @@ export function ChromatinViewport(props: { for (let i = 0; i < binsAmount; i++) { finalColors[i] = colors[finalColorIndices[i]]; } newColors[configurationDatumIndex] = data3D.cacheColorArray(finalColors); } } /* const mapScaleToChromatin = (values: Array<number>, scale: chroma.Scale): Array<Array<vec4>> => { const ratio = Math.max(...values); const valuesNormalized = values.map(v => v / ratio); const allColors: Array<Array<vec4>> = new Array(data3D.chromosomes.length); for (let chromosomeIndex = 0; chromosomeIndex < configuration.chromosomes.length; chromosomeIndex++) { const chromatinPart = viewport.getChromatinPartByChromosomeIndex(chromosomeIndex); if (!chromatinPart) { continue; } const chromosomeBinOffset = chromatineSlices[chromosomeIndex].from; const colors: Array<vec4> = valuesNormalized.slice(chromosomeBinOffset, chromosomeBinOffset + chromatinPart.getBinsPositions().length).map(v => { return scale(v).gl(); }); allColors[chromosomeIndex] = chromatinPart.cacheColorArray(colors); } return allColors; } if (configuration.colorMappingMode == '1d-density') { const data1d: Array<{ chromosome: string, from: number, to: number }> | null = data.data.find(d => d.id == isoDataID.wrap(configuration.mapValues.id))?.values as Sparse1DTextData | Sparse1DNumericData | null; if (!data1d) { return; } const scale = chroma.scale(['white', 'blue']); const countPerBin: Array<number> = Array(data3D.values.length); _.fill(countPerBin, 0) for (let chromosomeIndex = 0; chromosomeIndex < configuration.chromosomes.length; chromosomeIndex++) { const partInfo = chromatineSlices[chromosomeIndex]; const chromosomeData1d = data1d.filter(d => d.chromosome == partInfo.name); const res = data3D.basePairsResolution; for (let binIndex = 0; binIndex < partInfo.to - partInfo.from; binIndex++) { for (const datum of chromosomeData1d) { if (datum.from <= (binIndex + 1) * res && datum.to >= binIndex * res) { countPerBin[binIndex + partInfo.from] += 1; } } } } const logCountPerBin = countPerBin.map(v => Math.log(v) + 1); setInnerColors(() => mapScaleToChromatin(logCountPerBin, scale)); } if (configuration.colorMappingMode == '1d-numerical') { const data1d: Sparse1DNumericData | null = data.data.find(d => d.id == isoDataID.wrap(configuration.mapValues.id))?.values as Sparse1DNumericData | null; if (!data1d) { return; } const scale = chroma.scale(['white', 'blue']); const valuesPerBin: Array<Array<number>> = Array.from(Array(data3D.values.length), () => []) for (let chromosomeIndex = 0; chromosomeIndex < configuration.chromosomes.length; chromosomeIndex++) { const partInfo = chromatineSlices[chromosomeIndex]; const chromosomeData1d = data1d.filter(d => d.chromosome == partInfo.name); const res = data3D.basePairsResolution; for (let binIndex = 0; binIndex < partInfo.to - partInfo.from; binIndex++) { for (const datum of chromosomeData1d) { if (datum.from <= (binIndex + 1) * res && datum.to >= binIndex * res) { valuesPerBin[binIndex + partInfo.from].push(datum.value); } } } } const aggregationFunction: (n: Array<number>) => number | undefined = { "min": _.min, "max": _.max, "mean": _.mean, "median": median, "sum": _.sum }[configuration.mapValues.aggregationFunction] const aggregatedValuesPerBin = valuesPerBin.map((vs) => { const result = aggregationFunction(vs) if (result == null || isNaN(result)) { return 0; } return result; }) setInnerColors(() => mapScaleToChromatin(aggregatedValuesPerBin, scale)); } if (configuration.colorMappingMode == "centromers") { const mapData1D: Positions3D | null = data.data.find(d => d.id == isoDataID.wrap(configuration.mapValues.id))?.values as Positions3D | null; if (!mapData1D) { return; } const centromereBins = new Array(mapData1D.length); // Normalize centromeres to current bounding box const normalizeCenter = data3D.normalizeCenter; const normalizeScale = data3D.normalizeScale; const centromeres: Array<vec3> = []; for (const c of mapData1D) { let centromere = vec3.fromValues(c.x, c.y, c.z); centromere = vec3.sub(vec3.create(), centromere, normalizeCenter); centromere = vec3.scale(vec3.create(), centromere, normalizeScale); centromeres.push(centromere); } // Map centromere 3D position to 1D bin index for (let centromereIndex = 0; centromereIndex < centromeres.length; centromereIndex++) { let minDistance = 1.0; let minIndex = -1; for (let valueIndex = 0; valueIndex < data3D.values.length; valueIndex++) { const value = vec3.fromValues(data3D.values[valueIndex].x, data3D.values[valueIndex].y, data3D.values[valueIndex].z); const diff = vec3.sub(vec3.create(), centromeres[centromereIndex], value); const distance = vec3.dot(diff, diff); if (distance < minDistance) { minDistance = distance; minIndex = valueIndex; } } centromereBins[centromereIndex] = minIndex; } // Map bin to distance const distances: Array<number> = []; for (let valueIndex = 0; valueIndex < data3D.values.length; valueIndex++) { const distance = Math.min(...centromereBins.map((v) => Math.abs(v - valueIndex))); distances.push(distance); } // Color inside with mapping // const colorScale = chroma.scale('YlGnBu'); const colorScale = chroma.scale(['white', 'blue']); setInnerColors(() => mapScaleToChromatin(distances, colorScale)); } if (configuration.colorMappingMode == "linear-order") { const order: Array<number> = []; for (let chromosomeIndex = 0; chromosomeIndex < configuration.chromosomes.length; chromosomeIndex++) { const partInfo = chromatineSlices[chromosomeIndex]; for (let o = 0; o < partInfo.to - partInfo.from + 1; o++) { order.push(o); } } const colorScale = chroma.scale('YlGnBu'); //pick better color scale setInnerColors(() => mapScaleToChromatin(order, colorScale)); } if (configuration.colorMappingMode == 'sasa') { newColors[configurationDatumIndex] = chromatinPart.cacheColorArray(finalColors); } else if (configurationDatum.colorMappingMode == "sasa") { if (configuration.sasa.method == 'generated') { throw "Not implemented" throw "Not implemented"; } //TODO: per chromosome or whole chromosome const globalSasaValues: Array<number> = []; if (!configuration.sasa.individual) { globalSasaValues.push(...sasa(data3D.values, { method: configuration.sasa.method, probe_size: configuration.sasa.probeSize, }, configuration.sasa.accuracy)) } else { for (let chromosomeIndex = 0; chromosomeIndex < configuration.chromosomes.length; chromosomeIndex++) { const partInfo = chromatineSlices[chromosomeIndex]; const chromosomePositions = data3D.values.slice(partInfo.from, partInfo.to + 1); globalSasaValues.push(...sasa(chromosomePositions, { //TODO: per chromosome or whole chromosome const globalSasaValues = sasa(positions, { method: configuration.sasa.method, probe_size: configuration.sasa.probeSize, }, configuration.sasa.accuracy)); } }, configuration.sasa.accuracy); //TODO: fix underlying bug where the something sometimes don't contain last bin of the chromosome if (globalSasaValues.length < data3D.values.length) { globalSasaValues.push(globalSasaValues.reduce((a, b) => a + b, 0) / globalSasaValues.length); } } const colorScale = chroma.scale(['white', 'green']); setInnerColors(() => mapScaleToChromatin(globalSasaValues, colorScale)); } if (configuration.colorMappingMode == '3d-density') { //TODO: per chromosome or whole chromosome const densities: Array<number> = []; if (!configuration.density.individual) { densities.push(...density(data3D.values, configuration.density.probeSize)) } else { // if (globalSasaValues.length < chromatinPart.values.length) { // globalSasaValues.push(globalSasaValues.reduce((a, b) => a + b, 0) / globalSasaValues.length); // } for (let chromosomeIndex = 0; chromosomeIndex < configuration.chromosomes.length; chromosomeIndex++) { const partInfo = chromatineSlices[chromosomeIndex]; const chromosomePositions = data3D.values.slice(partInfo.from, partInfo.to + 1); densities.push(...density( chromosomePositions, configuration.density.probeSize // TODO: user setting )); } const colorScale = chroma.scale(['#fcfdfd', '#010a4e']); newColors[configurationDatumIndex] = chromatinPart.cacheColorArray(mapScaleToChromatin(chromatinPart, globalSasaValues, colorScale)); } //TODO: fix underlying bug where the data3d.values sometimes don't contain last bin of the chromosome if (densities.length < data3D.values.length) { console.warn("Fixing bullshit") console.log(densities) console.log(data3D.values) densities.push(densities.reduce((a, b) => a + b, 0) / densities.length); } const colorScale = chroma.scale(['white', 'red']); setInnerColors(() => mapScaleToChromatin(densities, colorScale)); } */ setColors(() => newColors); }, [viewport, globalSelections.selections, configuration.data, configuration.sasa, data.data, configuration.chromosomes, configuration.density]); Loading
app/src/components/viewports/TADViewport.tsx +60 −4 Original line number Diff line number Diff line Loading @@ -9,7 +9,8 @@ import { BinPosition, squareDiameter } from "../../modules/graphics"; import { SelectionAction, SelectionActionKind, SelectionState } from "../../modules/storage/models/selections"; import { DataAction, DataState, Positions3D } from "../../modules/storage/models/data"; import { useConfiguration, useSelections } from "../hooks"; import { sasa } from "../../modules/sasa"; import { ChartShallowDataShape, LineChart, LineSeries, ScatterPlot, SparklineChart } from 'reaviz'; function groupSubsequentNumbers(array: Array<number>): Array<Array<number>> { return array.reduce<Array<Array<number>>>((grouped, next) => { Loading Loading @@ -72,6 +73,8 @@ export function TADViewport(props: { const [binGroups, setBinGroups] = useState<Array<Array<number>>>([]); const [svgNumbers, setSvgNumbers] = useState<Array<JSX.Element>>([]); const [sasaValues, setSasaValues] = useState<Array<Array<number>>>([]); const updatePositions = () => { if (!viewport || !viewport.canvas) { return; Loading Loading @@ -111,7 +114,39 @@ export function TADViewport(props: { positions.push(vec4.fromValues(values[i].x, values[i].y, values[i].z, 1.0)); } const globalSasaValues = [sasa(values, { method: 'constant', probe_size: 0.02, }, 100)]; viewport.setPositions(positions); for(let lod = 1; lod < 32; lod++) { const size = viewport.globals.sizes[lod]; if (size === 0) { break; } const offset = viewport.globals.offsets[lod-1]; const values = []; for(let i = 0; i < size; i++) { const j = i; if (i == size - 1 && viewport.globals.sizes[lod - 1] % 2 !== 0) { values.push((globalSasaValues[lod-1][j] + globalSasaValues[lod-1][j + 1] + globalSasaValues[lod-1][j + 2]) * 0.33); break; } else { values.push((globalSasaValues[lod-1][j] + globalSasaValues[lod-1][j + 1]) * 0.5); } } globalSasaValues.push(values); console.log(globalSasaValues); } setSasaValues(() => globalSasaValues); break; } } Loading Loading @@ -574,6 +609,15 @@ export function TADViewport(props: { setSelectionsRanges(() => selectionsRanges); }, [viewport, data, allSelections, selections, viewport.currentLoD]); let maxSasa = 0; let normalizedSasaValues = null; let xSize = 0; if (sasaValues[viewport.currentLoD] && tracksBlock) { maxSasa = Math.max(...sasaValues[viewport.currentLoD]); normalizedSasaValues = sasaValues[viewport.currentLoD].map(v => (v / maxSasa)); xSize = tracksBlock.width / normalizedSasaValues.length; } return (<div style={{ width: '100%', height: '100%', overflow: 'hidden', position: 'relative' }}> <canvas ref={canvasElement} style={{ width: '100%', height: '100%', overflow: 'hidden', position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }} onClick={onClick}></canvas> <svg style={{ width: '100%', height: '100%', overflow: 'hidden', position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, pointerEvents: 'none' }}> Loading @@ -589,7 +633,7 @@ export function TADViewport(props: { )} {/* {svgNumbers} */} </svg> {(currentBinsAmount && tracksBlock && !isNaN(tracksBlock.top)) && (<div className={'topDiv'}> {(currentBinsAmount && tracksBlock && normalizedSasaValues && !isNaN(tracksBlock.top)) && (<div className={'topDiv'}> <div style={{ width: tracksBlock.width, position: 'absolute', Loading @@ -604,7 +648,8 @@ export function TADViewport(props: { height: '8px', display: 'grid', marginTop: '8px', gridTemplateColumns: 'repeat(' + currentBinsAmount + ', 1fr)' gridTemplateColumns: 'repeat(' + currentBinsAmount + ', 1fr)', position: 'absolute' }}> {selectionRange.map((range, index) => { return <div key={index} style={{ Loading @@ -615,6 +660,17 @@ export function TADViewport(props: { </div> } else { return <div key={selectionRangeIndex}></div> } })} <div style={{width: '100%', height: '40px' }}></div> <SparklineChart width={tracksBlock.width} height={80} data={ normalizedSasaValues.map((v, i) => { return { key: i, data: 1.0 - v } as ChartShallowDataShape } ) }> </SparklineChart> {/* <svg style={{marginTop: '40px'}} width={tracksBlock.width.toString()} height={"80"} viewBox={"0 0 " + tracksBlock.width.toString() + " 80"}> <path fill="none" stroke="blue" strokeWidth={"1"} d={"" + normalizedSasaValues.map((v, i) => { return (i == 0 ? "M" : " L") + (i * xSize).toString() + "," + (80 - v).toString(); })} /> </svg> */} </div> </div>)} </div> Loading