Skip to content

Commit

Permalink
feat: generate and display stocsy
Browse files Browse the repository at this point in the history
  • Loading branch information
hamed-musallam committed May 21, 2024
1 parent a8a5697 commit 9038734
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/component/1d/Chart1D.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import XAxis from './XAxis';
import DatabaseElements from './database/DatabaseElements';
import IntegralsSeries from './integral/IntegralsSeries';
import JGraph from './jCouplingGraph/JGraph';
import { Stocsy } from './matrix/Stocsy';
import MultiAnalysisRanges from './multiAnalysis/MultiAnalysisRanges';
import MultiplicityTrees from './multiplicityTree/MultiplicityTrees';
import { PeakEditionProvider } from './peaks/PeakEditionManager';
Expand Down Expand Up @@ -61,8 +62,10 @@ function Chart1D({ mode, width, height, margin, displayerKey }) {
<ExclusionZonesAnnotations />
<DatabaseElements />
<PeaksShapes />
<Stocsy />
<SpectraTracker />
<SpectrumInfoBlock />

<SimilarityTree />
<g className="container" style={{ pointerEvents: 'none' }}>
<XAxis showGrid mode={mode} />
Expand Down
146 changes: 146 additions & 0 deletions src/component/1d/matrix/Stocsy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { extent } from 'd3';
import { matrixToStocsy } from 'nmr-processing';
import { useEffect, useRef } from 'react';

import { useChartData } from '../../context/ChartContext';
import {
withExport,
withExportRegister,
} from '../../context/ExportPrepareContext';

Check failure on line 9 in src/component/1d/matrix/Stocsy.tsx

View workflow job for this annotation

GitHub Actions / nodejs / lint-check-types

Cannot find module '../../context/ExportPrepareContext' or its corresponding type declarations.
import { useScaleChecked } from '../../context/ScaleContext';
import { getYScaleWithRation } from '../utilities/scale';

import { useMatrix } from './useMatrix';

interface StocsyProps {
x: Float64Array | never[];
y: Float64Array;
color: string[];
yDomain: number[];
}

const componentId = 'stocsy';

export function Stocsy() {
const matrix = useMatrix();
if (!matrix) return null;
const { x, matrixY } = matrix;
const { color, y } = matrixToStocsy(matrixY, 0);
const yDomain = extent(y) as number[];

return (
<g>
<RenderStocsyAsCanvas x={x} y={y} color={color} yDomain={yDomain} />
<RenderStocsyAsSVG x={x} y={y} color={color} yDomain={yDomain} />
</g>
);
}

const RenderStocsyAsCanvas = withExportRegister((props: StocsyProps) => {
const { x, y, color, yDomain } = props;

const { width, margin, height } = useChartData();
const canvasRef = useRef<HTMLCanvasElement>(null);
const { scaleX } = useScaleChecked();

const scaleY = getYScaleWithRation({
height,
yDomain,
scaleRatio: 1,
margin,
});

const { right, bottom } = margin;
const canvasWidth = width - right;
const canvasHeight = height - bottom;

useEffect(() => {
const canvas = canvasRef.current;
const ctx = canvas?.getContext('2d');

if (ctx) {
ctx.fillStyle = 'background-color: transparent';
}

// Function to draw lines in a rectangle
function drawLinesInRect(x1, x2, y1, y2, color) {
if (!ctx) return;
ctx.strokeStyle = color;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(x1, x2);
ctx.lineTo(y1, y2);
ctx.stroke();
}

let index = 0;
for (const val of x) {
if (index === x.length - 1) {
break;
}
const nextIndex = index + 1;

const x1 = scaleX()(val);
const y1 = scaleY(y[index]);
const x2 = scaleX()(x[nextIndex]);
const y2 = scaleY(y[nextIndex]);

drawLinesInRect(x1, y1, x2, y2, color[index]);
index++;
}

return () => {
if (ctx && canvas) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
};
}, [color, height, scaleX, scaleY, width, x, y]);

return (
<foreignObject
{...{
width: canvasWidth,
height: canvasHeight,
}}
data-no-export="true"
>
<canvas
ref={canvasRef}
{...{ width: canvasWidth, height: canvasHeight }}
/>
</foreignObject>
);
}, componentId);

const RenderStocsyAsSVG = withExport((props: StocsyProps) => {
const { x, y, color, yDomain } = props;
const { margin, height } = useChartData();
const { scaleX } = useScaleChecked();

const scaleY = getYScaleWithRation({
height,
yDomain,
scaleRatio: 1,
margin,
});

return Array.from(x).map((val, index) => {
if (index === x.length - 1 || typeof val !== 'number') {
return null;
}
const nextIndex = index + 1;
const x1 = scaleX()(val);
const y1 = scaleY(y[index]);
const x2 = scaleX()(x[nextIndex]);
const y2 = scaleY(y[nextIndex]);

return (
<path
key={`${val}`}
d={`M${x1},${y1} L${x2},${y2} Z`}
fill="transparent"
stroke={color[index]}
/>
);
});
}, componentId);
58 changes: 58 additions & 0 deletions src/component/1d/matrix/useMatrix.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Spectrum } from 'nmr-load-save';

import { isSpectrum1D } from '../../../data/data1d/Spectrum1D';
import { useChartData } from '../../context/ChartContext';
import useSpectraByActiveNucleus from '../../hooks/useSpectraPerNucleus';

interface MatrixColor {
color: string;
start: number;
end: number;
}

export function interpolateNumber(
inputRange: [number, number],
outputRange: [number, number],
) {
return (value) =>
outputRange[0] +
((value - inputRange[0]) * (outputRange[1] - outputRange[0])) /
(inputRange[1] - inputRange[0]);
}

export function mapMatrixColors(colors) {
if (colors.length === 0) return [];

const result: MatrixColor[] = [];
let start = 0;

for (let i = 1; i <= colors.length; i++) {
if (colors[i] !== colors[start]) {
result.push({ color: colors[start], start, end: i - 1 });
start = i;
}
}

return result;
}

function getX(spectra: Spectrum[]) {
const spectrum = spectra?.[0];

if (!spectrum || !isSpectrum1D(spectrum)) return [];
return spectrum.data.x;
}

export function useMatrix() {
const { displayerMode } = useChartData();
const spectra = useSpectraByActiveNucleus();

if (displayerMode !== '1D') return null;

return {
x: getX(spectra) || [],
matrixY: spectra.map((spectrum) =>
isSpectrum1D(spectrum) ? spectrum.data.re : [],
),
};
}
9 changes: 9 additions & 0 deletions src/component/1d/utilities/scale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ function getIntegralYScale(options: IntegralYScaleOptions) {
[height * 0.3, margin.top + height * 0.1],
);
}
function getYScaleWithRation(options: IntegralYScaleOptions) {
const { height, margin, yDomain, scaleRatio } = options;
const [min, max] = yDomain;
return scaleLinear(
[min * scaleRatio, max * scaleRatio],
[height - margin.bottom, margin.top],
);
}

function reScaleY(scale: number, { domain, height, margin }) {
const _scale = scaleLinear(domain, [height - margin.bottom, margin.top]);
Expand Down Expand Up @@ -119,4 +127,5 @@ export {
getYScale,
getIntegralYScale,
reScaleY,
getYScaleWithRation,
};

0 comments on commit 9038734

Please sign in to comment.