Skip to content

Commit f872eaa

Browse files
committed
feat: wip: implement scale conversion
for `HorizontalAxis1D` Closes: #4040
1 parent 3eb317b commit f872eaa

10 files changed

Lines changed: 91 additions & 40 deletions

File tree

src/component/1d/HorizontalAxis1D.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,14 @@ export function HorizontalAxis1D() {
2020
const isInset = useIsInset();
2121
const isExportingProcessStart = useCheckExportStatus();
2222

23-
const { unit, allowedUnits, setUnit } = useHorizontalAxisUnit();
23+
const { unit, allowedUnits, setUnit, domain } = useHorizontalAxisUnit();
2424
const unitLabel = axisUnitToLabel[unit];
2525

2626
const refAxis = useRef<SVGGElement>(null);
2727

2828
const scaler = useMemo(() => {
29-
// TODO apply unit conversion
30-
return scaleX(null);
31-
}, [scaleX]);
29+
return scaleX({ customDomain: domain });
30+
}, [domain, scaleX]);
3231
const { ticks, scale: ticksScale } = useLinearPrimaryTicks(
3332
scaler,
3433
'horizontal',

src/component/1d/Line.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ function Line({ data, id, display, index }: LineProps) {
2929

3030
const paths = useMemo(() => {
3131
const _scaleX = scaleX();
32-
const _scaleY = scaleY(id);
32+
const _scaleY = scaleY({ spectrumId: id });
3333

3434
const pathBuilder = new PathBuilder();
3535
if (data?.x && data?.y && _scaleX(0)) {

src/component/1d/multiplicityTree/MultiplicityTree.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export default function MultiplicityTree(props: MultiplicityTreeProps) {
4444
if (!spectrum || !isSpectrum1D(spectrum)) return null;
4545
const { from, to } = range;
4646
const maxY = getMaxY(spectrum, { from, to });
47-
const startY = scaleY(spectrum.id)(maxY) - marginBottom;
47+
const startY = scaleY({ spectrumId: spectrum.id })(maxY) - marginBottom;
4848

4949
const tree = generateTreeNodes(range, spectrum);
5050

src/component/1d/peaks/PeakAnnotations.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ function PeakAnnotation({
178178
const { scaleX, scaleY } = useScaleChecked();
179179

180180
const sx = scaleX()(x);
181-
const sy = scaleY(spectrumKey)(y) - 5;
181+
const sy = scaleY({ spectrumId: spectrumKey })(y) - 5;
182182

183183
const { y1, y2 } = getLineYCoordinates(sign, isOverlap);
184184

src/component/1d/peaks/PeakEditionManager.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export function PeakEditionProvider({ children }: Required<PropsWithChildren>) {
9393
}
9494
y = py + dy;
9595
if (useScaleY) {
96-
y = scaleY(spectrum?.id)(py) + dy;
96+
y = scaleY({ spectrumId: spectrum?.id })(py) + dy;
9797
}
9898

9999
if (x + InputDimension.width > width) {

src/component/1d/peaks/usePeakShapesPath.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export function usePeakShapesPath(spectrum: Spectrum1D) {
5353
break;
5454
}
5555
const _scaleX = scaleX();
56-
const _scaleY = scaleY(spectrum.id);
56+
const _scaleY = scaleY({ spectrumId: spectrum.id });
5757

5858
const pathBuilder = new PathBuilder();
5959
let fill = 'transparent';

src/component/1d/tool/PeakPointer.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ function PeakPointer() {
6565
if (!closePeak) return null;
6666

6767
const x = scaleX()(closePeak.x);
68-
const y = scaleY(activeSpectrum.id)(closePeak.y) - spectrumIndex * shiftY;
68+
const y =
69+
scaleY({ spectrumId: activeSpectrum.id })(closePeak.y) -
70+
spectrumIndex * shiftY;
6971

7072
return (
7173
<div

src/component/1d/utilities/scale.ts

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { scaleLinear } from 'd3-scale';
22
import { useCallback } from 'react';
33

44
import { useChartData } from '../../context/ChartContext.js';
5+
import type { ScaleLinearNumberOptions } from '../../context/ScaleContext.tsx';
56
import type {
67
Domains,
78
Margin,
@@ -16,7 +17,7 @@ interface ScaleInsetXOptions {
1617
xDomain: number[];
1718
mode: SpectraDirection;
1819
}
19-
interface ScaleXOptions extends ScaleInsetXOptions {
20+
interface ScaleXOptions extends ScaleInsetXOptions, ScaleLinearNumberOptions {
2021
xDomains: Domains;
2122
}
2223

@@ -27,7 +28,7 @@ interface InsetYScaleOptions {
2728
spectraBottomMargin: number;
2829
}
2930

30-
interface ScaleYOptions extends InsetYScaleOptions {
31+
interface ScaleYOptions extends InsetYScaleOptions, ScaleLinearNumberOptions {
3132
yDomains: Domains;
3233
verticalAlign: VerticalAlignment;
3334
}
@@ -47,34 +48,40 @@ function getInsetYScale(options: InsetYScaleOptions) {
4748
return scaleLinear(yDomain, [innerHeight, margin.top]);
4849
}
4950

50-
function getXScale(
51-
options: ScaleXOptions,
52-
spectrumId: number | null | string = null,
53-
) {
54-
const { width, margin, xDomains, xDomain, mode } = options;
51+
function getXScale(options: ScaleXOptions) {
52+
const { width, margin, xDomains, xDomain, mode, spectrumId, customDomain } =
53+
options;
54+
5555
const range =
5656
mode === 'RTL'
5757
? [width - margin.right, margin.left]
5858
: [margin.left, width - margin.right];
59-
return scaleLinear(spectrumId ? xDomains[spectrumId] : xDomain, range);
59+
60+
let domain = spectrumId ? xDomains[spectrumId] : xDomain;
61+
if (customDomain) {
62+
domain = customDomain;
63+
}
64+
return scaleLinear(domain, range);
6065
}
6166

62-
function getYScale(
63-
options: ScaleYOptions,
64-
spectrumId: number | null | string = null,
65-
) {
67+
function getYScale(options: ScaleYOptions) {
6668
const {
6769
height,
6870
margin,
6971
verticalAlign,
7072
yDomain,
7173
yDomains,
7274
spectraBottomMargin = 10,
75+
spectrumId = null,
76+
customDomain,
7377
} = options;
7478
let domainY: number[] = yDomain;
7579
if (spectrumId && yDomains?.[spectrumId]) {
7680
domainY = yDomains[spectrumId];
7781
}
82+
if (customDomain) {
83+
domainY = customDomain;
84+
}
7885
const [min, max] = domainY;
7986
let bottomShift = spectraBottomMargin;
8087

@@ -126,7 +133,7 @@ function useScaleX() {
126133
return getInsetXScale({ margin, mode, width, xDomain });
127134
}
128135

129-
return getXScale({ margin, mode, width, xDomain, xDomains }, spectrumId);
136+
return getXScale({ margin, mode, width, xDomain, xDomains, spectrumId });
130137
},
131138
[isInset, margin, mode, width, xDomain, xDomains],
132139
);

src/component/context/ScaleContext.tsx

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,12 @@ import { useVerticalAlign } from '../hooks/useVerticalAlign.js';
1414

1515
import { useChartData } from './ChartContext.js';
1616

17+
export interface ScaleLinearNumberOptions {
18+
spectrumId?: number | null | string;
19+
customDomain?: number[];
20+
}
1721
type ScaleLinearNumberFunction = (
18-
spectrumId?: number | null | string,
22+
options?: ScaleLinearNumberOptions,
1923
) => ScaleLinear<number, number, number>;
2024

2125
interface ScaleState {
@@ -59,7 +63,7 @@ export function ScaleProvider({ children }: Required<PropsWithChildren>) {
5963
const isInset = useIsInset();
6064

6165
const scaleX = useCallback<ScaleLinearNumberFunction>(
62-
(spectrumId = null) => {
66+
(options) => {
6367
if (isInset) {
6468
return getInsetXScale({
6569
width,
@@ -69,28 +73,26 @@ export function ScaleProvider({ children }: Required<PropsWithChildren>) {
6973
});
7074
}
7175

72-
return getXScale({ width, margin, xDomains, xDomain, mode }, spectrumId);
76+
return getXScale({ ...options, width, margin, xDomains, xDomain, mode });
7377
},
7478
[isInset, margin, mode, width, xDomain, xDomains],
7579
);
7680

7781
const scaleY = useCallback<ScaleLinearNumberFunction>(
78-
(spectrumId = null) => {
82+
(options) => {
7983
if (isInset) {
8084
return getInsetYScale({ height, yDomain, margin, spectraBottomMargin });
8185
}
8286

83-
return getYScale(
84-
{
85-
height,
86-
margin,
87-
yDomains,
88-
yDomain,
89-
verticalAlign,
90-
spectraBottomMargin,
91-
},
92-
spectrumId,
93-
);
87+
return getYScale({
88+
...options,
89+
height,
90+
margin,
91+
yDomains,
92+
yDomain,
93+
verticalAlign,
94+
spectraBottomMargin,
95+
});
9496
},
9597
[
9698
spectraBottomMargin,

src/component/hooks/use_axis_unit.ts

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@ import { match } from 'ts-pattern';
2323
import { isSpectrum2D } from '../../data/data2d/Spectrum2D/index.ts';
2424
import { useChartData } from '../context/ChartContext.tsx';
2525
import { useDispatch } from '../context/DispatchContext.tsx';
26+
import { useScaleChecked } from '../context/ScaleContext.tsx';
2627

2728
import { useActiveNucleusTab } from './useActiveNucleusTab.ts';
29+
import { useActiveSpectra } from './useActiveSpectra.ts';
30+
import { useActiveSpectrum } from './useActiveSpectrum.ts';
2831
import useSpectrum from './useSpectrum.ts';
2932
import { useVisibleSpectra1D } from './use_visible_spectra_1d.ts';
3033

@@ -43,16 +46,54 @@ function assertIn<V, T extends V>(value: V, values: T[]): asserts value is T {
4346

4447
export function useHorizontalAxisUnit() {
4548
const spectra = useVisibleSpectra1D();
49+
const activeSpectra = useActiveSpectra();
50+
const firstActiveSpectrum = useMemo(() => {
51+
const firstSelected = activeSpectra?.find((s) => s.selected);
52+
return firstSelected
53+
? spectra.find((s) => s.id === firstSelected.id)
54+
: spectra[0];
55+
}, [activeSpectra, spectra]);
56+
const { scaleX } = useScaleChecked();
57+
4658
const { nucleus, nucleusUnits } = useAxisUnit1D();
4759
const dispatch = useDispatch();
4860

49-
const mode: keyof Nucleus1DUnit['horizontal'] = spectra[0]?.info.isFt
61+
const mode: keyof Nucleus1DUnit['horizontal'] = firstActiveSpectrum?.info.isFt
5062
? 'ft'
5163
: 'fid';
5264
const unit: AxisUnit1DFid | AxisUnit1DFt = nucleusUnits.horizontal[mode];
5365
const allowedUnits: AxisUnit1DFid[] | AxisUnit1DFt[] =
5466
mode === 'ft' ? axisUnits1DFt : axisUnits1DFid;
5567

68+
const domain = useMemo(() => {
69+
function getPtDomain() {
70+
return [0, firstActiveSpectrum?.data.x.length ?? 0];
71+
}
72+
73+
return match(mode)
74+
.with('fid', () =>
75+
match(unit)
76+
.with('s', () => undefined)
77+
.with('pt', getPtDomain)
78+
.run(),
79+
)
80+
.with('ft', () =>
81+
match(unit)
82+
.with('ppm', () => undefined)
83+
.with('pt', getPtDomain)
84+
.with('hz', () => {
85+
if (!firstActiveSpectrum) return undefined;
86+
87+
const scale = scaleX();
88+
return scale
89+
.domain()
90+
.map((v) => v * firstActiveSpectrum.info.originFrequency);
91+
})
92+
.run(),
93+
)
94+
.exhaustive();
95+
}, [firstActiveSpectrum, mode, scaleX, unit]);
96+
5697
const setUnit = useCallback(
5798
(unit: AxisUnit) => {
5899
match(mode)
@@ -75,7 +116,7 @@ export function useHorizontalAxisUnit() {
75116
[dispatch, mode, nucleus],
76117
);
77118

78-
return { mode, unit, allowedUnits, setUnit };
119+
return { mode, unit, allowedUnits, setUnit, domain };
79120
}
80121

81122
export function useDirectAxisUnit() {

0 commit comments

Comments
 (0)