ABTSoftware / SciChart.JS.Examples

MIT License
76 stars 36 forks source link

Clearing /Removing Series with RolloverModifier causing error #82

Closed texas697 closed 3 years ago

texas697 commented 3 years ago

same issue as this ticket. https://github.com/ABTSoftware/SciChart.JS.Examples/issues/78

"scichart": "1.4.1566"

`export default function Chart() {
  const [sciChartSurface, setSciChartSurface] = React.useState<SciChartSurface>();
  const [wasmContext, setWasmContext] = React.useState<TSciChart>();

  const dataSeriesMapRef = React.useRef<Map<keyof typeof chartData, XyDataSeries>>();

  React.useEffect(() => {
    (async () => {
      const { sciChartSurface, wasmContext } = await drawChart(theme, chartType);
      setSciChartSurface(sciChartSurface);
      setWasmContext(wasmContext);
    })();

    dataSeriesMapRef.current = new Map<keyof typeof chartData, XyDataSeries>();

    return () => sciChartSurface?.delete();
  }, [chartType]); // make sure the chart is initialized only once

  React.useEffect(() => {
    if (dataSeriesMapRef.current && sciChartSurface && wasmContext && !isLoading) {
      // const currentSeries = sciChartSurface.renderableSeries.asArray();
      // if (currentSeries) sciChartSurface.renderableSeries.clear();
      updateChartWithData(dataSeriesMapRef.current, theme, wasmContext, sciChartSurface, chartData, chartType);
    }
  }, [theme, chartData, wasmContext, sciChartSurface, isLoading]);

  return (
    <div id="chart-container">
      <ControlsLegend
        chartData={chartData}
        dataSeriesMap={dataSeriesMapRef.current as Map<keyof typeof chartData, XyDataSeries>}
        theme={theme}
        sciChartSurface={sciChartSurface as SciChartSurface}
        wasmContext={wasmContext as TSciChart}
      />
      <div id={DIVID} style={{ width: windowWidth, height: windowHeight }} />
      <ControlsBottom />
    </div>
  );
}`

"drawChart.js"

`export default async (theme: ExtendedTheme, chartType: string) => {
  const { sciChartSurface, wasmContext } = await SciChartSurface.create(DIVID);
  const isLightTheme = theme.palette.type === 'light';

  console.log('createChart');
  const xAxis = new NumericAxis(wasmContext);
  const yAxis = new NumericAxis(wasmContext);

  xAxis.labelProvider.formatLabel = (unixTimestamp: number) => {
    return new Date(unixTimestamp * 1000).toLocaleDateString('en-us', {
      month: 'short',
      year: 'numeric',
      day: 'numeric',
    });
  };

  yAxis.labelProvider = new CustomLabelProvider();

  sciChartSurface.yAxes.add(yAxis);
  sciChartSurface.xAxes.add(xAxis);

  if (chartType !== 'stack') sciChartSurface.chartModifiers.add(new RolloverModifier({ showRolloverLine: false }));
  sciChartSurface.chartModifiers.add(new ZoomPanModifier());
  sciChartSurface.chartModifiers.add(new ZoomExtentsModifier());
  sciChartSurface.chartModifiers.add(new MouseWheelZoomModifier());
  sciChartSurface.chartModifiers.add(new PinchZoomModifier());

  const rubberBandXyZoomModifier = new RubberBandXyZoomModifier({
    isAnimated: true,
    animationDuration: 400,
    easingFunction: easing.outExpo,
    fill: '#FFFFFF33',
    stroke: '#FFFFFF77',
    strokeThickness: 1,
  });
  sciChartSurface.chartModifiers.add(rubberBandXyZoomModifier);

  sciChartSurface.zoomExtents();

  if (isLightTheme) sciChartSurface.applyTheme(new SciChartJSLightTheme());
  else sciChartSurface.applyTheme(new SciChartJSDarkTheme());
  return { sciChartSurface, wasmContext };
};`

"updateChartWithData.js"

`export const updateChartWithData = (
  dataSeriesMap: Map<keyof typeof chartData, XyDataSeries>,
  theme: ExtendedTheme,
  wasmContext: TSciChart,
  sciChartSurface: SciChartSurface,
  chartData: ChartData,
  chartType: string
) => {
  sciChartSurface.invalidateElement();
  const streamIds = Object.keys(chartData);
  const isLightTheme = theme.palette.type === 'light';

  if (isLightTheme) sciChartSurface.applyTheme(new SciChartJSLightTheme());
  else sciChartSurface.applyTheme(new SciChartJSDarkTheme());

  sciChartSurface.invalidateElement();
  console.log('updateChartWithData');
  console.log(chartData);

  const stackedColumns: any[] = [];
  streamIds.forEach((s) => {
    const streamId = Number(s) as keyof ChartData;
    const streamSelectorId = streamId as keyof typeof chartData;
    if (dataSeriesMap.has(streamSelectorId)) {
      const xyDataSeries = dataSeriesMap.get(streamSelectorId);
      chartData[streamId].data.forEach((value) => {
        xyDataSeries?.append(value.timestamp, value.value);
      });
    } else if (chartType === 'scatter') {
      const lineSeries = LineChart(dataSeriesMap, theme, wasmContext, chartData, streamSelectorId, streamId);

      sciChartSurface.renderableSeries.add(lineSeries);
      sciChartSurface.zoomExtents();
    } else if (chartType === 'bar') {
      const lineSeries = BarChart(dataSeriesMap, theme, wasmContext, chartData, streamSelectorId, streamId);
      sciChartSurface.renderableSeries.add(lineSeries);
      sciChartSurface.zoomExtents();
    } else {
      const lineSeries = StackedChart(dataSeriesMap, theme, wasmContext, chartData, streamSelectorId, streamId);
      stackedColumns.push(lineSeries);
    }
  });
};`

"LineChart.js"

`export default (
  dataSeriesMap: Map<keyof typeof chartData, XyDataSeries>,
  theme: ExtendedTheme,
  wasmContext: TSciChart,
  chartData: ChartData,
  streamSelectorId: keyof typeof chartData,
  streamId: keyof ChartData
) => {
  const xyDataSeries = new XyDataSeries(wasmContext);
  dataSeriesMap.set(streamSelectorId, xyDataSeries);

  const obj = chartData[streamSelectorId];
  const stroke = obj.stroke as string;
  xyDataSeries.dataSeriesName = obj.label as string;

  chartData[streamId].data.forEach((value) => {
    xyDataSeries.append(value.timestamp, value.value);
  });

  const lineSeries = new FastLineRenderableSeries(wasmContext, {
    stroke,
    strokeThickness: obj.strokeThickness,
    strokeDashArray: obj.strokeDashArray,
    dataSeries: xyDataSeries,
    animation: new WaveAnimation({ zeroLine: -1, pointDurationFraction: 0.5, duration: 1000 }),
  });

  lineSeries.rolloverModifierProps.tooltipColor = theme.palette.background.paper;
  lineSeries.rolloverModifierProps.tooltipTextColor = theme.palette.getContrastText(stroke);
  lineSeries.rolloverModifierProps.markerColor = stroke;
  lineSeries.rolloverModifierProps.tooltipTemplate = (
    id: string,
    tooltipProps: RolloverModifierRenderableSeriesProps,
    seriesInfo: SeriesInfo,
    updateSize: (width: number, height: number) => void
  ) => {
    const { tooltipColor, tooltipTextColor, markerColor } = tooltipProps;
    const { formattedXValue, yValue, seriesName } = seriesInfo;
    const width = 192;
    const height = 60;

    updateSize(width, height);
    return `<svg width='${width}' height='${height}' class="root">
        <rect width="100%" height="100%" fill='${tooltipColor}' stroke='${markerColor}' stroke-width='2' />
         <svg width='100%'>
           <text dy="0" fill='${tooltipTextColor}'>
            <tspan x="15" y="20" class="title">${formattedXValue}</tspan>
             <tspan x="15" y="45" class="value">
              ${seriesName} | ${yValue.toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
             </tspan>
          </text>
        </svg>
      </svg>`;
  };
  return lineSeries;
};`

Also if possible maybe you can see why the tooltip is not updating the styles after changing themes. It doesn't make the change until I change chart types. I assume that is because the Chart is being re-created. :-).

The logic is based from this closed ticket. https://github.com/ABTSoftware/SciChart.JS.Examples/issues/57

klishevich commented 3 years ago

Hi @texas697

I was not able to run you example, but I tried a similar one where on change I clear RenderableSeries. Please note the sciChartSurface.renderableSeries.clear() just removes all renderable series from the SciChartSurface. To prevent memory leaks you also need to delete renderableSeries and dataSeries.

        sciChartSurface.renderableSeries.asArray().forEach(rs => {
            rs.dataSeries.delete();
            rs.delete();
        });
        sciChartSurface.renderableSeries.clear();

        const newDataSeries = new OhlcDataSeries(wasmContext, {
            xValues: getXValues(interval),
            openValues,
            highValues,
            lowValues,
            closeValues
        });
        const newRenderableSeries = new FastCandlestickRenderableSeries(wasmContext, {
            strokeThickness: 2,
            dataSeries: newDataSeries,
            dataPointWidth: 0.5,
            paletteProvider: new MyPaletteProvider(917222400, 917827200),
            opacity: 0.75
        });
        sciChartSurface.renderableSeries.add(newRenderableSeries);

My example works without any problem. If you are still experiencing this issue please provide the sample project I can just install and run to reproduce the problem.

andyb1979 commented 3 years ago

Point of discussion:

Should RenderableSeries.clear() also delete? We could potentially make this an optional flag in .clear() e.g.

sciChartSurface.renderableSeries.clear(deleteChildren: boolean  = true);

On Wed, Apr 28, 2021 at 11:04 AM Michael Klishevich < @.***> wrote:

Hi @texas697 https://github.com/texas697

I was not able to run you example, but I tried a similar one where on change I clear RenderableSeries. Please note the sciChartSurface.renderableSeries.clear() just removes all renderable series from the SciChartSurface. To prevent memory leaks you also need to delete renderableSeries and dataSeries.

    sciChartSurface.renderableSeries.asArray().forEach(rs => {
        rs.dataSeries.delete();
        rs.delete();
    });
    sciChartSurface.renderableSeries.clear();

    const newDataSeries = new OhlcDataSeries(wasmContext, {
        xValues: getXValues(interval),
        openValues,
        highValues,
        lowValues,
        closeValues
    });
    const newRenderableSeries = new FastCandlestickRenderableSeries(wasmContext, {
        strokeThickness: 2,
        dataSeries: newDataSeries,
        dataPointWidth: 0.5,
        paletteProvider: new MyPaletteProvider(917222400, 917827200),
        opacity: 0.75
    });
    sciChartSurface.renderableSeries.add(newRenderableSeries);

My example works without any problem. If you are still experiencing this issue please provide the sample project I can just install and run to reproduce the problem.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/ABTSoftware/SciChart.JS.Examples/issues/82#issuecomment-828329661, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADLEDVLYS3G2KSDBYXVHWGDTK7MTJANCNFSM43TZAXBA .

klishevich commented 3 years ago

Hi @texas697 In fact there was a bug, when removing/clearing renderableSeries with RolloverModifier added to SciChartSurface, we have fixed it in version 1.4.1578.