Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: plot mass with labels #309

Merged
merged 6 commits into from
Nov 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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