import React, { useState } from 'react'; import { Story, Meta } from '@storybook/react/types-6-0'; import PlotLegend from '../../components/plotControls/PlotLegend'; import { PlotLegendGradientProps } from '../../components/plotControls/PlotGradientLegend'; import ScatterPlot from '../../plots/ScatterPlot'; import { min, max } from 'lodash'; import { dataSetDivergingGradient, dataSetSequentialDiscrete, processInputData, } from '../plots/ScatterPlot.storyData'; import { gradientDivergingColorscaleMap } from '../../types/plots/addOns'; import { VEuPathDBScatterPlotData } from '../plots/ScatterPlot.storyData'; import { PlotLegendProps } from '../../components/plotControls/PlotLegend'; import SliderWidget, { SliderWidgetProps, } from '../../components/widgets/Slider'; // A collection of stories for viewing our Diverging Gradient Colormap export default { title: 'Colors/Gradient Diverging', component: ScatterPlot, argTypes: { nPoints: { control: { type: 'range', min: 5, max: 300, step: 5 }, }, }, } as Meta; // set some default props const plotWidth = 1000; const plotHeight = 600; const independentAxisLabel = 'independent axis label'; const dependentAxisLabel = 'dependent axis label'; // Create a template for the sequential colormap stories. // Show a scatterplot with overlay, as well as legend so we can see the colormap. interface TemplateProps { data: VEuPathDBScatterPlotData; plotLegendProps: PlotLegendProps; nPoints?: number; } const Template: Story = (args) => { const { dataSetProcess: dataSetProcessGradient } = processInputData( args.data, 'scatterplot', 'markers', 'number', 'number', false ); // Reduce the dataset to only nPoints points so that we can see how the colormap looks when the // scatter plot is or isn't overplotted. // Points without both x and y aren't drawn, so only need to remove x coordinate. dataSetProcessGradient.series[0].x = dataSetProcessGradient.series[0].x.slice( 0, args.nPoints ); // Find the y axis min and max const [yMin, yMax] = [ min(dataSetProcessGradient.series[0].y), max(dataSetProcessGradient.series[0].y), ]; // Opacity slider state const [markerBodyOpacity, setMarkerBodyOpacity] = useState(1); // Opacity slider coloring const opacityColorSpecProps: SliderWidgetProps['colorSpec'] = { type: 'gradient', tooltip: '#aaa', knobColor: '#aaa', trackGradientStart: '#fff', trackGradientEnd: '#000', }; // For each colormap, show the legend, an example plot, and our opacity slider. return (
{ setMarkerBodyOpacity(newValue); }} containerStyles={{ height: 100, width: 425, marginLeft: 75, }} showLimits={true} label={'Marker opacity'} disabled={false} colorSpec={opacityColorSpecProps} />
); }; // Showcase the continuous version of the diverging gradient colormap. Overlay values are drawn from // a continuous distribution // Setup gradient colorscale legend const gradientLegendProps = { legendMax: max( dataSetDivergingGradient.scatterplot.data[0] .seriesGradientColorscale as number[] ), legendMin: min( dataSetDivergingGradient.scatterplot.data[0] .seriesGradientColorscale as number[] ), gradientColorscaleType: 'divergent', // MUST be odd! nTicks: 5, showMissingness: false, legendTitle: 'legend', }; export const Continuous = Template.bind({}); Continuous.args = { data: dataSetDivergingGradient, plotLegendProps: { type: 'colorscale', ...(gradientLegendProps as PlotLegendGradientProps), }, nPoints: 120, }; // Showcase discretized version of the diverging gradient colormap. For this story, // the overlay var is a low cardinality, equidistant set of integers. // Set up discretized legend. const vocabularyEquidistant = ['3', '2', '1', '0', '-1', '-2', '-3']; // Modify the sequential version since it already has the integers let dataSetDivergingDiscrete: VEuPathDBScatterPlotData = JSON.parse( JSON.stringify(dataSetSequentialDiscrete) ); dataSetDivergingDiscrete.scatterplot.data[0].seriesGradientColorscale?.forEach( (val, index, arr) => { arr[index] = Number(val) - 4; } ); let legendItems = vocabularyEquidistant.map((label) => { return { label, marker: 'square', markerColor: gradientDivergingColorscaleMap( +label / max(vocabularyEquidistant.map(Number))! ), hasData: true, group: 1, rank: 1, }; }); export const Discrete = Template.bind({}); Discrete.args = { data: dataSetDivergingDiscrete, plotLegendProps: { type: 'list', legendItems: legendItems, checkedLegendItems: vocabularyEquidistant, showOverlayLegend: true, }, nPoints: 120, }; // Showcase discretized version of the diverging gradient colormap. For this story, // the overlay var is a low cardinality variable with non-uniform spacing between neighboring values. // Set up discretized legend. const vocabularyNonUniform = ['-10', '-9.2', '-1', '0', '1.1', '3', '27']; legendItems = vocabularyNonUniform.map((label) => { return { label, marker: 'square', markerColor: gradientDivergingColorscaleMap( +label / (max(vocabularyNonUniform.map(Number))! - 1) ), hasData: true, group: 1, rank: 1, }; }); // Replace a few values in the original data so that we have values that are not uniform from their neighbors. let dataSetDivergingDiscreteNonUniform: VEuPathDBScatterPlotData = JSON.parse( JSON.stringify(dataSetDivergingDiscrete) ); dataSetDivergingDiscreteNonUniform.scatterplot.data[0].seriesGradientColorscale?.forEach( (val, index, arr) => { arr[index] = (+val) ** 3; } ); export const DiscreteNonUniform = Template.bind({}); DiscreteNonUniform.args = { data: dataSetDivergingDiscreteNonUniform, plotLegendProps: { type: 'list', legendItems: legendItems, checkedLegendItems: vocabularyNonUniform, showOverlayLegend: true, }, nPoints: 120, };