Skip to content

Commit 4a7e50f

Browse files
feat: default peak shape preferences
1 parent 3b11b29 commit 4a7e50f

8 files changed

Lines changed: 160 additions & 58 deletions

File tree

src/component/1d/BrushTracker1D.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ export function BrushTracker1D({ children }: Required<PropsWithChildren>) {
9494
'matrixGeneration',
9595
activeTab,
9696
);
97+
const { defaultPeakShape } = usePanelPreferences('peaks', activeTab);
98+
9799
const { logger } = useLogger();
98100
const scaleState = useScaleChecked();
99101
const convertToPPM = usePixelToPPMConverter();
@@ -205,12 +207,14 @@ export function BrushTracker1D({ children }: Required<PropsWithChildren>) {
205207
});
206208
break;
207209
}
208-
case options.peakPicking.id:
210+
case options.peakPicking.id: {
211+
const { startX, endX } = brushData;
209212
dispatch({
210213
type: 'ADD_PEAKS',
211-
payload: brushData,
214+
payload: { startX, endX, defaultPeakShape },
212215
});
213216
break;
217+
}
214218
case options.databaseRangesSelection.id:
215219
propagateEvent();
216220
break;
@@ -304,6 +308,7 @@ export function BrushTracker1D({ children }: Required<PropsWithChildren>) {
304308
logger,
305309
dispatchPreferences,
306310
activeTab,
311+
defaultPeakShape,
307312
openAnalysisModal,
308313
width,
309314
height,
@@ -420,13 +425,14 @@ export function BrushTracker1D({ children }: Required<PropsWithChildren>) {
420425
switch (keyModifiers) {
421426
case primaryKeyIdentifier: {
422427
switch (selectedTool) {
423-
case 'peakPicking':
428+
case 'peakPicking': {
429+
const { x } = event;
424430
dispatch({
425431
type: 'ADD_PEAK',
426-
payload: event,
432+
payload: { x, defaultPeakShape },
427433
});
428434
break;
429-
435+
}
430436
case 'integral':
431437
dispatch({
432438
type: 'CUT_INTEGRAL',
@@ -494,6 +500,7 @@ export function BrushTracker1D({ children }: Required<PropsWithChildren>) {
494500
},
495501
[
496502
activeTab,
503+
defaultPeakShape,
497504
dispatch,
498505
dispatchPreferences,
499506
getModifiersKey,

src/component/header/AutoPeakPickingOptionPanel.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ import { useToaster } from '../context/ToasterContext.js';
88
import Label from '../elements/Label.js';
99
import { NumberInput2Controller } from '../elements/NumberInput2Controller.js';
1010
import { Select2Controller } from '../elements/Select2Controller.js';
11+
import { useActiveNucleusTab } from '../hooks/useActiveNucleusTab.ts';
1112
import {
1213
MIN_AREA_POINTS,
1314
useCheckPointsNumberInWindowArea,
1415
} from '../hooks/useCheckPointsNumberInWindowArea.js';
16+
import { usePanelPreferences } from '../hooks/usePanelPreferences.ts';
1517

1618
import { headerLabelStyle } from './Header.js';
1719
import { HeaderWrapper } from './HeaderWrapper.js';
@@ -60,6 +62,8 @@ export function AutoPeakPickingOptionPanel() {
6062
const dispatch = useDispatch();
6163
const pointsNumber = useCheckPointsNumberInWindowArea();
6264
const toaster = useToaster();
65+
const nucleus = useActiveNucleusTab();
66+
const { defaultPeakShape } = usePanelPreferences('peaks', nucleus);
6367
const {
6468
handleSubmit,
6569
formState: { isValid },
@@ -74,7 +78,10 @@ export function AutoPeakPickingOptionPanel() {
7478
if (pointsNumber > MIN_AREA_POINTS) {
7579
dispatch({
7680
type: 'AUTO_PEAK_PICKING',
77-
payload: values,
81+
payload: {
82+
options: values,
83+
defaultPeakShape,
84+
},
7885
});
7986
} else {
8087
toaster.show({

src/component/modal/EditPeakShapeModal.tsx

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,12 @@ import { useActiveNucleusTab } from '../hooks/useActiveNucleusTab.js';
1818
import { usePanelPreferences } from '../hooks/usePanelPreferences.js';
1919
import { formatNumber } from '../utility/formatNumber.js';
2020

21-
2221
const FooterContainer = styled.div`
2322
display: flex;
2423
justify-content: flex-end;
2524
gap: 5px;
2625
`;
2726

28-
2927
type Shape = NonNullable<Peak1D['shape']>;
3028

3129
type Kind =
@@ -49,9 +47,9 @@ function getValues(peak: Peak1D, kind: Kind): Shape {
4947
const shapeData =
5048
(shape?.kind || '').toLocaleLowerCase() !== kind
5149
? {
52-
...getKindDefaultValues(kind),
53-
...(shape?.fwhm && { fwhm: shape?.fwhm }),
54-
}
50+
...getKindDefaultValues(kind),
51+
...(shape?.fwhm && { fwhm: shape?.fwhm }),
52+
}
5553
: shape;
5654

5755
return shapeData as Shape;
@@ -64,7 +62,7 @@ function validation(kind: Kind) {
6462
});
6563
}
6664

67-
const KINDS: Array<{ label: string; value: Kind }> = [
65+
export const PEAKS_SHAPES: Array<{ label: string; value: Kind }> = [
6866
{
6967
value: 'gaussian',
7068
label: 'Gaussian',
@@ -114,9 +112,7 @@ function InnerEditPeakShapeModal(props: Required<EditPeakShapeModalProps>) {
114112
});
115113

116114
function changePeakShapeHandler(applyToAll = false) {
117-
118115
void handleSubmit((values) => {
119-
120116
dispatch({
121117
type: 'CHANGE_PEAK_SHAPE',
122118
payload: {
@@ -127,9 +123,7 @@ function InnerEditPeakShapeModal(props: Required<EditPeakShapeModalProps>) {
127123
},
128124
});
129125
onCloseDialog();
130-
131126
})();
132-
133127
}
134128

135129
function handleChangeKind({ value }: { value: Kind }) {
@@ -150,7 +144,7 @@ function InnerEditPeakShapeModal(props: Required<EditPeakShapeModalProps>) {
150144
<>
151145
<Label title="Kind:" style={labelStyle}>
152146
<Select2
153-
items={KINDS}
147+
items={PEAKS_SHAPES}
154148
selectedItemValue={kind}
155149
onItemSelect={handleChangeKind}
156150
/>
@@ -177,15 +171,23 @@ function InnerEditPeakShapeModal(props: Required<EditPeakShapeModalProps>) {
177171
)}
178172
</>
179173
</StyledDialogBody>
180-
<DialogFooter >
181-
<FooterContainer >
182-
<Button variant="outlined" intent="danger" onClick={() => onCloseDialog?.()}>
174+
<DialogFooter>
175+
<FooterContainer>
176+
<Button
177+
variant="outlined"
178+
intent="danger"
179+
onClick={() => onCloseDialog?.()}
180+
>
183181
Cancel
184182
</Button>
185183
<Button intent="primary" onClick={() => changePeakShapeHandler()}>
186184
Apply
187185
</Button>
188-
<Button intent="success" data-action="apply" onClick={() => changePeakShapeHandler(true)} >
186+
<Button
187+
intent="success"
188+
data-action="apply"
189+
onClick={() => changePeakShapeHandler(true)}
190+
>
189191
Apply to all
190192
</Button>
191193
</FooterContainer>

src/component/panels/PeaksPanel/PeaksPanel.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,7 @@ function PeaksPanelInner(props: PeaksPanelInnerProps) {
160160
displayingMode === 'spread' ? 'Top of the peak' : 'Top of the spectrum',
161161
onClick: toggleDisplayingMode,
162162
active: displayingMode === 'spread',
163-
}
164-
163+
},
165164
];
166165

167166
return (

src/component/panels/PeaksPanel/PeaksPreferences.tsx

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
import { forwardRef, memo, useCallback, useMemo } from 'react';
2-
import { useForm } from 'react-hook-form';
2+
import { useForm, useWatch } from 'react-hook-form';
33

44
import { usePreferences } from '../../context/PreferencesContext.js';
5+
import { fieldLabelStyle } from '../../elements/FormatField.tsx';
6+
import { GroupPane } from '../../elements/GroupPane.tsx';
7+
import Label from '../../elements/Label.tsx';
8+
import { NumberInput2Controller } from '../../elements/NumberInput2Controller.tsx';
9+
import { Select2Controller } from '../../elements/Select2Controller.tsx';
510
import useNucleus from '../../hooks/useNucleus.js';
611
import { usePanelPreferencesByNuclei } from '../../hooks/usePanelPreferences.js';
12+
import { PEAKS_SHAPES } from '../../modal/EditPeakShapeModal.tsx';
713
import { getUniqueNuclei } from '../../utility/getUniqueNuclei.js';
814
import type { NucleusPreferenceField } from '../extra/preferences/NucleusPreferences.js';
915
import { NucleusPreferences } from '../extra/preferences/NucleusPreferences.js';
@@ -101,17 +107,75 @@ export default memo(
101107
});
102108

103109
useSettingImperativeHandle(ref, handleSubmit, saveHandler);
110+
const currentPreferences = useWatch({ control, name: 'nuclei' });
104111

105112
return (
106113
<PreferencesContainer>
107-
{nuclei?.map((n) => (
108-
<NucleusPreferences
109-
control={control}
110-
key={n}
111-
nucleus={n}
112-
fields={formatFields}
113-
/>
114-
))}
114+
{nuclei?.map((n) => {
115+
const kind = currentPreferences?.[n].defaultPeakShape?.kind;
116+
return (
117+
<NucleusPreferences
118+
control={control}
119+
key={n}
120+
nucleus={n}
121+
fields={formatFields}
122+
renderBottom={() => (
123+
<GroupPane
124+
text="Default peak shape"
125+
style={{ header: { color: '#005d9e' } }}
126+
>
127+
<Label title="Kind:" style={fieldLabelStyle}>
128+
<Select2Controller
129+
items={PEAKS_SHAPES}
130+
control={control}
131+
name={`nuclei.${n}.defaultPeakShape.kind`}
132+
/>
133+
</Label>
134+
135+
<Label title="FWHM:" style={fieldLabelStyle}>
136+
<NumberInput2Controller
137+
min={0}
138+
control={control}
139+
name={`nuclei.${n}.defaultPeakShape.fwhm`}
140+
controllerProps={{
141+
rules: { required: true },
142+
defaultValue: 1,
143+
}}
144+
/>
145+
</Label>
146+
147+
{kind === 'pseudoVoigt' && (
148+
<Label title="Mu:" style={fieldLabelStyle}>
149+
<NumberInput2Controller
150+
min={0}
151+
control={control}
152+
name={`nuclei.${n}.defaultPeakShape.mu`}
153+
controllerProps={{
154+
rules: { required: true },
155+
defaultValue: 0.5,
156+
}}
157+
/>
158+
</Label>
159+
)}
160+
{kind === 'generalizedLorentzian' && (
161+
<Label title="Gamma:" style={fieldLabelStyle}>
162+
<NumberInput2Controller
163+
min={-1}
164+
max={2}
165+
control={control}
166+
name={`nuclei.${n}.defaultPeakShape.gamma`}
167+
controllerProps={{
168+
rules: { required: true },
169+
defaultValue: 0.5,
170+
}}
171+
/>
172+
</Label>
173+
)}
174+
</GroupPane>
175+
)}
176+
/>
177+
);
178+
})}
115179
</PreferencesContainer>
116180
);
117181
}),

0 commit comments

Comments
 (0)