Skip to content

Commit

Permalink
fix: plot mass with labels (#309)
Browse files Browse the repository at this point in the history
  • Loading branch information
lpatiny authored Nov 4, 2022
1 parent 800a5a1 commit 1d45352
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 80 deletions.
89 changes: 41 additions & 48 deletions src/app/components/MeasurementMassPlot.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { xyToXYObject } from 'ml-spectra-processing';
import { getBestPeaks, getPeaks } from 'ms-spectrum';
import { getBestPeaks, Spectrum } from 'ms-spectrum';
import { useMemo } from 'react';
import {
Annotation,
Expand All @@ -21,6 +21,25 @@ interface Peak {
}

export function MeasurementMassPlot(props: MeasurementPlotProps) {
const { measurement } = props;
if (!measurement.data) {
throw new Error(
'This is weird, the data property is not available on measurement',
);
}
if (measurement.data.length === 0) {
throw new Error('Data property is empty');
}
if (measurement.data.length > 1) {
throw new Error('Length of data property is larger than 1');
}
if (!measurement.data[0].variables.x) {
throw new Error('x variable in undefined');
}
if (!measurement.data[0].variables.y) {
throw new Error('y variable in undefined');
}

return (
<PlotController>
<MassComponent {...props} />
Expand All @@ -29,77 +48,51 @@ export function MeasurementMassPlot(props: MeasurementPlotProps) {
}

function MassComponent(props: MeasurementPlotProps) {
const {
measurement,
dataIndex = 0,
xVariableName = 'x',
yVariableName = 'y',
} = props;
const { measurement } = props;

const { data } = measurement;
const xAxis = `${xVariableName}-x`;
const yAxis = `${yVariableName}-y`;
const { x, y } = useMemo(() => {
const { variables } = data[dataIndex];
const { [xVariableName]: x, [yVariableName]: y } = variables;
if (x === undefined || y === undefined) {
throw new Error(
`Variable "${
x === undefined ? xVariableName : yVariableName
}" is not available in data. Only ${Object.keys(
data[dataIndex].variables,
).join(', ')} are available`,
);
}
const { variables } = data[0];

return { x, y };
}, [data, dataIndex, xVariableName, yVariableName]);
const { x, y } = useMemo(() => {
return { x: variables.x, y: variables.y };
}, [variables]);

const { x: xDomain } = usePlotControllerAxes();
const { profile, peaks } = useMemo(() => {
const profile = xyToXYObject({
const spectrum = new Spectrum({
x: x.data,
y: y.data,
});
const isContinuous = spectrum.isContinuous();
const profile =
isContinuous &&
xyToXYObject({
x: x.data,
y: y.data,
});
return {
profile,
peaks: getPeaks(profile),
peaks: spectrum.getPeaks(profile),
};
}, [x.data, y.data]);
const bestPeaks = useMemo(
() =>
getBestPeaks(peaks, {
from: xDomain?.min ?? Number.NEGATIVE_INFINITY,
to: xDomain?.max ?? Number.POSITIVE_INFINITY,
limit: 5,
from: xDomain?.min,
to: xDomain?.max,
limit: 10,
numberSlots: 10,
threshold: 0.01,
}),
[peaks, xDomain?.max, xDomain?.min],
);
return (
<BasicComponent {...props}>
<LineSeries
data={profile}
lineStyle={{ stroke: 'green' }}
xAxis={xAxis}
yAxis={yAxis}
/>

<BarSeries
data={peaks}
xAxis={xAxis}
yAxis={yAxis}
lineStyle={{ stroke: 'red' }}
/>
{profile && <LineSeries data={profile} lineStyle={{ stroke: 'green' }} />}
<BarSeries data={peaks} lineStyle={{ stroke: 'red' }} />
<Annotations>
{bestPeaks.map(({ x, y, shortLabel }: Peak) => (
<Annotation.Group
key={x}
x={x}
y={y}
horizontalAlign="none"
verticalAlign="none"
>
<Annotation.Group key={x} x={x} y={y}>
<Annotation.Line
x1="0"
x2="0"
Expand Down
4 changes: 0 additions & 4 deletions src/app/components/MeasurementPlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ function MeasurementComponent(props: MeasurementPlotProps) {
yVariableName = 'y',
} = props;

const xAxis = `${xVariableName}-x`;
const yAxis = `${yVariableName}-y`;
const { x, y } = useMemo(() => {
const { variables } = data[dataIndex];
const { [xVariableName]: x, [yVariableName]: y } = variables;
Expand All @@ -66,8 +64,6 @@ function MeasurementComponent(props: MeasurementPlotProps) {
x: x.data,
y: y.data,
})}
xAxis={xAxis}
yAxis={yAxis}
/>
</BasicComponent>
);
Expand Down
14 changes: 2 additions & 12 deletions src/app/components/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ export function BasicComponent(
} = props;
const { title = '', data } = measurement;

const xAxis = `${xVariableName}-x`;
const yAxis = `${yVariableName}-y`;
const { x, y } = useMemo(() => {
const { variables } = data[dataIndex];
const { [xVariableName]: x, [yVariableName]: y } = variables;
Expand All @@ -59,27 +57,21 @@ export function BasicComponent(
}, [data, dataIndex, xVariableName, yVariableName]);
const direction = new Set(['vertical', 'horizontal']);
const rectZoom = useRectangularZoom({
horizontalAxisId: xAxis,
verticalAxisId: yAxis,
disabled: zoom !== 'rectangular',
});
const axisZoom = useAxisZoom({
direction: zoom === 'vertical' ? 'vertical' : 'horizontal',
horizontalAxisId: xAxis,
verticalAxisId: yAxis,
disabled: !direction.has(zoom),
});
useAxisWheelZoom({
direction: wheelZoom === 'vertical' ? 'vertical' : 'horizontal',
axisId: wheelZoom === 'vertical' ? yAxis : xAxis,
axisId: wheelZoom === 'vertical' ? 'y' : 'x',
disabled: !direction.has(wheelZoom),
});
const crossHairAnnot = useCrossHair({
horizontalAxisId: xAxis,
verticalAxisId: yAxis,
disabled: !crossHair,
});
usePan({ horizontalAxisId: xAxis, verticalAxisId: yAxis });
usePan();

return (
<div
Expand All @@ -104,15 +96,13 @@ export function BasicComponent(
{crossHairAnnot.annotations}
</Annotations>
<Axis
id={xAxis}
hidden={!showHorizontalAxis}
displayPrimaryGridLines={showVerticalGrid}
flip={flipHorizontalAxis}
position="bottom"
label={`${x.label}${x.units ? `(${x.units})` : ''}`}
/>
<Axis
id={yAxis}
hidden={!showVerticalAxis}
displayPrimaryGridLines={showHorizontalGrid}
position="left"
Expand Down
12 changes: 6 additions & 6 deletions tests/components/MeasurementExplorer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ test.describe('MeasurementExplorer', () => {
await component.locator('_react=MeasurementPlot[dataIndex=1]').isEnabled();
await expect(
component.locator('_react=MeasurementPlot[xVariableName="a"]'),
).toBeEnabled();
).toBeVisible();
await expect(
component.locator('_react=MeasurementPlot[yVariableName="t"]'),
).toBeEnabled();
).toBeVisible();
});
test('reverse btn', async ({ mount }) => {
const component = await mount(
Expand All @@ -67,10 +67,10 @@ test.describe('MeasurementExplorer', () => {
// test MeasurementPlot props after reverse
await expect(
component.locator('_react=MeasurementPlot[xVariableName="y"]'),
).toBeEnabled();
).toBeVisible();
await expect(
component.locator('_react=MeasurementPlot[yVariableName="x"]'),
).toBeEnabled();
).toBeVisible();
});
test('flip btn', async ({ mount }) => {
const component = await mount(
Expand All @@ -86,13 +86,13 @@ test.describe('MeasurementExplorer', () => {
);

// test MeasurementPlot props before flipping axis
await expect(noFlippedPlot).toBeEnabled();
await expect(noFlippedPlot).toBeVisible();
await expect(FlippedPlot).toHaveCount(0);

await flipBtn.click();

// test MeasurementPlot props after flipping axis
await expect(noFlippedPlot).toHaveCount(0);
await expect(FlippedPlot).toBeEnabled();
await expect(FlippedPlot).toBeVisible();
});
});
23 changes: 13 additions & 10 deletions tests/components/MeasurementPlot.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,14 @@ test.describe('MeasurementPlot', () => {
await expect(xAxis).toContainText('1/CM');
await expect(yAxis).toContainText('% Transmittance');
await expect(component).toContainText('Mattson Instruments');
await expect(component.locator('_react=Axis[id="x-x"]')).toBeEnabled();
await expect(component.locator('_react=Axis[id="y-y"]')).toBeEnabled();

await expect(xAxis).toBeVisible();
await expect(yAxis).toBeVisible();

// axis

await expect(component.locator('_react=Axis[id=/-x$/]')).toHaveText(/.+/);
await expect(component.locator('_react=Axis[id=/-y$/]')).toHaveText(/.+/);
await expect(xAxis).toHaveText(/.+/);
await expect(yAxis).toHaveText(/.+/);

// flip
await component
Expand All @@ -59,7 +60,7 @@ test.describe('MeasurementPlot', () => {

// crossHair
await component.hover({ position: { x: 200, y: 200 } });
await expect(component.locator('_react=Text')).toBeEnabled();
await expect(component.locator('_react=Text')).toBeVisible();
await expect(component.locator('_react=Line')).toHaveCount(2);
// grids

Expand Down Expand Up @@ -124,8 +125,8 @@ test.describe('MeasurementPlot', () => {
const xAxis = component.locator('_react=Axis >> nth=0');
const yAxis = component.locator('_react=Axis >> nth=1');

await expect(component.locator('_react=Axis[id="y-x"]')).toBeEnabled();
await expect(component.locator('_react=Axis[id="a-y"]')).toBeEnabled();
await expect(xAxis).toBeVisible();
await expect(yAxis).toBeVisible();

await expect(xAxis).toContainText('TRANSMITTANCE');
await expect(yAxis).toContainText('Absorbance');
Expand All @@ -135,7 +136,7 @@ test.describe('MeasurementPlot', () => {
<MeasurementPlot measurement={irMeasurement} flipHorizontalAxis />,
);
const xAxis = component.locator('_react=Axis[flip=true] >> nth=0');
await expect(xAxis).toBeEnabled();
await expect(xAxis).toBeVisible();
});
test('remove horizontal axis', async ({ mount }) => {
const component = await mount(
Expand All @@ -145,14 +146,16 @@ test.describe('MeasurementPlot', () => {
/>,
);

await expect(component.locator('_react=Axis[id=/-x$/]')).toHaveText('');
const xAxis = component.locator('_react=Axis >> nth=0');
await expect(xAxis).toHaveText('');
});
test('remove vertical axis', async ({ mount }) => {
const component = await mount(
<MeasurementPlot measurement={irMeasurement} showVerticalAxis={false} />,
);

await expect(component.locator('_react=Axis[id=/-y$/]')).toHaveText('');
const yAxis = component.locator('_react=Axis >> nth=1');
await expect(yAxis).toHaveText('');
});
test('vertical zoom', async ({ mount }) => {
const component = await mount(
Expand Down

0 comments on commit 1d45352

Please sign in to comment.