Skip to content

Commit

Permalink
fix: add peaks in manual range picking (#2668)
Browse files Browse the repository at this point in the history
* fix: add peaks in manual range picking

do not use multilplet-analysis in

* chore: fix detectSignal test case

* chore: fix changeRange

* chore: use mapRanges in changeRange function

* chore: pre-release of nmr-processing

* fix: prevent useAssignment context to throw error with empty key

* chore: add blueprintjs (#2705)

* chore: stable release of nmr-processing

* chore: update package-lock

* fix: display the range with no signal correctly in the table

* test: add range with signals

---------

Co-authored-by: hamed musallam <hamed.musallam@gmail.com>
Co-authored-by: hamed-musallam <35760236+hamed-musallam@users.noreply.github.com>
  • Loading branch information
3 people committed Nov 24, 2023
1 parent 72447eb commit 50de770
Show file tree
Hide file tree
Showing 14 changed files with 795 additions and 485 deletions.
875 changes: 604 additions & 271 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/component/1d/Viewer1D.tsx
Expand Up @@ -11,7 +11,7 @@ import {
import { ResponsiveChart } from 'react-d3-utils';
import { useOnOff } from 'react-science/ui';

import { MAX_LENGTH } from '../../data/data1d/Spectrum1D/ranges/detectSignal';
import { MAX_LENGTH } from '../../data/data1d/Spectrum1D/ranges/detectSignals';
import BrushXY, { BRUSH_TYPE } from '../1d-2d/tools/BrushXY';
import CrossLinePointer from '../1d-2d/tools/CrossLinePointer';
import { ViewerResponsiveWrapper } from '../2d/Viewer2D';
Expand Down
12 changes: 6 additions & 6 deletions src/component/2d/zones/Signal.tsx
Expand Up @@ -23,9 +23,9 @@ function Signal({ signal }: SignalProps) {
return [id].concat(buildID(id, 'X'), buildID(id, 'Y'));
}, []);

const assignment = useAssignment(signal.id);
const assignment = useAssignment(signal?.id || '');
const { showSignals, showPeaks } = useActiveSpectrumZonesViewState();
const highlight = useHighlight(buildIDs(assignment.id));
const highlight = useHighlight(buildIDs(assignment?.id));
const highlightData = useHighlightData();

const [isHighlighted, setIsHighlighted] = useState(false);
Expand All @@ -35,14 +35,14 @@ function Signal({ signal }: SignalProps) {
highlightData.highlight.highlighted.some((_highlighted) =>
buildIDs(signal.id).includes(_highlighted),
) ||
assignment.isActive
assignment?.isActive
) {
setIsHighlighted(true);
} else {
setIsHighlighted(false);
}
}, [
assignment.isActive,
assignment?.isActive,
buildIDs,
highlightData.highlight.highlighted,
signal.id,
Expand All @@ -57,11 +57,11 @@ function Signal({ signal }: SignalProps) {
<SignalCrosshair signal={signal} />
<circle
onMouseEnter={() => {
assignment.show();
assignment?.show();
highlight.show();
}}
onMouseLeave={() => {
assignment.hide();
assignment?.hide();
highlight.hide();
}}
key={signal.id}
Expand Down
73 changes: 48 additions & 25 deletions src/component/assignment/AssignmentsContext.ts
Expand Up @@ -26,7 +26,7 @@ export interface AssignmentsData
id: string;
isActive: boolean;
isOver: boolean;
assigned: Record<Axis, string[]>;
assigned: Partial<Record<Axis, string[]>>;
removeAll: (axis: Axis) => void;
toggle: (atomIDs: string[], dimension: AssignmentDimension) => void;
setActive: (axis: Axis) => void;
Expand Down Expand Up @@ -64,9 +64,10 @@ export function useAssignment(key: number | string): AssignmentsData {
dispatch,
} = useAssignmentData();

if ((typeof key !== 'string' && typeof key !== 'number') || key === '') {
if (!['string', 'number'].includes(typeof key)) {
throw new Error(`assignment key must be a non-empty string or number`);
}

const id = String(key);

const isActive = useMemo(() => {
Expand All @@ -77,51 +78,73 @@ export function useAssignment(key: number | string): AssignmentsData {
}, [highlighted?.id, id]);

const assigned = useMemo(() => {
return assignments[id] || null;
if (!(id in assignments)) {
return {};
}

return assignments[id];
}, [assignments, id]);

const removeAll = useCallback(
(axis: Axis) => {
dispatch({
type: 'REMOVE',
payload: { ids: [id], axis },
});
if (id) {
dispatch({
type: 'REMOVE',
payload: { ids: [id], axis },
});
return true;
}
return false;
},
[dispatch, id],
);

const toggle = useCallback(
(atomIDs: string[], dimension: AssignmentDimension) => {
dispatch({
type: 'TOGGLE',
payload: { atomIDs, id, dimension },
});
if (id) {
dispatch({
type: 'TOGGLE',
payload: { atomIDs, id, dimension },
});
return true;
}
return false;
},
[dispatch, id],
);

const setActive = useCallback(
(axis: Axis) => {
dispatch({
type: 'SET_ACTIVE',
payload: {
id,
axis,
},
});
if (id) {
dispatch({
type: 'SET_ACTIVE',
payload: {
id,
axis,
},
});
return true;
}

return false;
},
[dispatch, id],
);

const show = useCallback(
(axis?: Axis) => {
dispatch({
type: 'SHOW',
payload: {
id,
axis,
},
});
if (id) {
dispatch({
type: 'SHOW',
payload: {
id,
axis,
},
});

return true;
}
return false;
},
[dispatch, id],
);
Expand Down
11 changes: 8 additions & 3 deletions src/component/panels/RangesPanel/RangesTableRow.tsx
Expand Up @@ -91,9 +91,12 @@ function RangesTableRow({
assignmentRange.assigned?.x || [],
{ type: HighlightEventSource.RANGE },
);
const assignmentSignal = useAssignment(rowData.tableMetaInfo.id);
const assignmentSignal = useAssignment(rowData?.tableMetaInfo?.id || '');

const highlightSignal = useHighlight(
[assignmentSignal.id].concat(assignmentSignal.assigned?.x || []),
assignmentSignal?.id
? [assignmentSignal.id].concat(assignmentSignal.assigned?.x || [])
: [],
{ type: HighlightEventSource.SIGNAL },
);
const highlightData = useHighlightData();
Expand Down Expand Up @@ -253,7 +256,9 @@ function RangesTableRow({
)}
{preferences.showMultiplicity && (
<td {...onHoverSignal}>
{lodashGet(rowData, 'tableMetaInfo.signal.multiplicity', '')}
{!rowData?.tableMetaInfo?.signal
? 'm'
: lodashGet(rowData, 'tableMetaInfo.signal.multiplicity', '')}
</td>
)}

Expand Down
16 changes: 10 additions & 6 deletions src/component/panels/RangesPanel/TableColumns/ActionsColumn.tsx
Expand Up @@ -62,12 +62,16 @@ function ActionsColumn({
<Fragment>
{showKind && (
<td {...onHoverSignal}>
<Select
onChange={changeRangeSignalKind}
items={SIGNAL_KINDS}
defaultValue={row.tableMetaInfo.signal.kind}
style={selectBoxStyle}
/>
{!row?.tableMetaInfo?.signal ? (
''
) : (
<Select
onChange={changeRangeSignalKind}
items={SIGNAL_KINDS}
defaultValue={row?.tableMetaInfo?.signal?.kind}
style={selectBoxStyle}
/>
)}
</td>
)}
{showActions && (
Expand Down
33 changes: 20 additions & 13 deletions src/component/panels/RangesPanel/TableColumns/SignalDeltaColumn.tsx
Expand Up @@ -12,7 +12,7 @@ function SignalDeltaColumn({
rowSpanTags,
}: RangeColumnProps) {
const dispatch = useDispatch();
const signal = row.tableMetaInfo.signal;
const signal = row?.tableMetaInfo?.signal;

function saveHandler(event) {
dispatch({
Expand All @@ -25,21 +25,28 @@ function SignalDeltaColumn({
});
}

if (!signal) return <td>{''}</td>;
const rangeText = `${formatNumber(row.from, format)} - ${formatNumber(
row.to,
format,
)}`;

if (!signal || !checkMultiplicity(signal.multiplicity, ['m'])) {
return (
<td {...rowSpanTags} {...onHover}>
{rangeText}
</td>
);
}

return (
<td {...rowSpanTags} {...onHover}>
{!checkMultiplicity(signal.multiplicity, ['m']) ? (
`${formatNumber(row.from, format)} - ${formatNumber(row.to, format)}`
) : (
<EditableColumn
value={formatNumber(signal.delta, format)}
onSave={saveHandler}
type="number"
style={{ padding: '0.1rem 0.4rem' }}
validate={(val) => val !== ''}
/>
)}
<EditableColumn
value={formatNumber(signal.delta, format)}
onSave={saveHandler}
type="number"
style={{ padding: '0.1rem 0.4rem' }}
validate={(val) => val !== ''}
/>
</td>
);
}
Expand Down
4 changes: 4 additions & 0 deletions src/component/panels/RangesPanel/hooks/useMapRanges.ts
Expand Up @@ -16,6 +16,10 @@ function useMapRanges(data) {
rangesData.push({
rowKey: v4(),
...range,
tableMetaInfo: {
...range.tableMetaInfo,
rowIndex: i,
},
});
} else if (range.signals.length === 1) {
const signal = range.signals[0];
Expand Down
12 changes: 6 additions & 6 deletions src/data/data1d/Spectrum1D/ranges/__tests__/detectSignal.test.ts
@@ -1,14 +1,14 @@
import { rangesToXY } from 'nmr-processing';
import { describe, it, expect } from 'vitest';

import detectSignal from '../detectSignal';
import detectSignals from '../detectSignals';

describe('lineBroadening', () => {
it('simple x, re, im to 1 Hz exp.', () => {
const from = 0.98;
const to = 1.02;
const frequency = 400;
const { x, y: re } = rangesToXY(
const { x, y } = rangesToXY(
[
{
from,
Expand All @@ -24,10 +24,10 @@ describe('lineBroadening', () => {
],
{ from: from - 0.02, to: to + 0.02, nbPoints: 512, frequency },
);
const result = detectSignal(
{ x: new Float64Array(x), re },
{ from, to, frequency },
);
const result = detectSignals(
{ x: new Float64Array(x), y },
{ from, to, frequency, nucleus: '1H' },
)[0];
expect(result?.multiplicity).toBe('t');
expect(result?.delta).toBeCloseTo(1, 3);
expect(result?.js[0].coupling).toBeCloseTo(7.2, 2);
Expand Down
55 changes: 26 additions & 29 deletions src/data/data1d/Spectrum1D/ranges/addRange.ts
Expand Up @@ -5,7 +5,7 @@ import { Signal1D, mapRanges } from 'nmr-processing';

import { DATUM_KIND } from '../../../constants/signalsKinds';

import detectSignal from './detectSignal';
import detectSignals from './detectSignals';

interface RangeOptions {
from: number;
Expand All @@ -16,51 +16,48 @@ export function createRangeObj({
from,
to,
absolute,
signal,
}: RangeOptions & { signal: Omit<Signal1D, 'id'>; absolute: number }) {
signals,
}: RangeOptions & { signals: Array<Omit<Signal1D, 'id'>>; absolute: number }) {
return {
id: v4(),
from,
to,
absolute, // the real value,
signals: [{ id: v4(), ...signal }],
signals: signals.map((signal) => ({ id: v4(), ...signal })),
kind: DATUM_KIND.signal,
integration: 0,
};
}

export function addRange(spectrum: Spectrum1D, options: RangeOptions) {
const { from, to } = options;
const { x, re } = spectrum.data;
const absolute = xyIntegration({ x, y: re }, { from, to, reverse: true });
const { x, re: y } = spectrum.data;
const { nucleus, originFrequency: frequency } = spectrum.info;

// detectSignal use the advance multiplet-analysis that can crash if too many points
const signal = detectSignal(
{ x, re },
{
from,
to,
frequency: spectrum.info.originFrequency,
},
);
const absolute = xyIntegration({ x, y }, { from, to, reverse: true });

let range;
const signals =
detectSignals(
{ x, y },
{
from,
to,
nucleus,
frequency,
},
) || [];

if (signal) {
range = createRangeObj({
from,
to,
absolute,
signal,
});
}
const range = {
from,
to,
absolute,
signals,
};

try {
if (range) {
spectrum.ranges.values = spectrum.ranges.values.concat(
mapRanges([range], spectrum),
);
}
spectrum.ranges.values = spectrum.ranges.values.concat(
mapRanges([range], spectrum),
);
} catch (error) {
reportError(error);
throw new Error('Could not calculate the multiplicity');
Expand Down

0 comments on commit 50de770

Please sign in to comment.