# Source code for qwt.plot_curve

    # -*- coding: utf-8 -*-
    #
    # Licensed under the terms of the Qwt License
    # Copyright (c) 2002 Uwe Rathmann, for the original C++ code
    # Copyright (c) 2015 Pierre Raybaut, for the Python translation/optimization
    # (see LICENSE file for more details)

    """
    QwtPlotCurve
    ------------

    .. autoclass:: QwtPlotCurve
       :members:
    """

    from qwt.text import QwtText
    from qwt.plot import QwtPlot, QwtPlotItem, QwtPlotItem_PrivateData
    from qwt._math import qwtSqr
    from qwt.graphic import QwtGraphic
    from qwt.plot_series import (
        QwtPlotSeriesItem,
        QwtSeriesStore,
        QwtSeriesData,
        QwtPointArrayData,
    )
    from qwt.symbol import QwtSymbol
    from qwt.plot_directpainter import QwtPlotDirectPainter
    from qwt.qthelpers import qcolor_from_str

    from qtpy import PYSIDE2
    from qtpy.QtGui import QPen, QBrush, QPainter, QPolygonF, QColor
    from qtpy.QtCore import QSize, Qt, QRectF, QPointF

    if PYSIDE2:
        import shiboken2
        import ctypes

    import numpy as np

    def qwtUpdateLegendIconSize(curve):
        if curve.symbol() and curve.testLegendAttribute(QwtPlotCurve.LegendShowSymbol):
            sz = curve.symbol().boundingRect().size()
            sz += QSize(2, 2)
            if curve.testLegendAttribute(QwtPlotCurve.LegendShowLine):
                w = np.ceil(1.5 * sz.width())
                if w % 2:
                    w += 1
                sz.setWidth(max([8, w]))
            curve.setLegendIconSize(sz)

    def qwtVerifyRange(size, i1, i2):
        if size < 1:
            return 0
        i1 = max([0, min([i1, size - 1])])
        i2 = max([0, min([i2, size - 1])])
        if i1 > i2:
            i1, i2 = i2, i1
        return i2 - i1 + 1

    def array2d_to_qpolygonf(xdata, ydata):
        """
        Utility function to convert two 1D-NumPy arrays representing curve data
        (X-axis, Y-axis data) into a single polyline (QtGui.PolygonF object).
        This feature is compatible with PyQt4, PyQt5 and PySide2 (requires QtPy).

        License/copyright: MIT License © Pierre Raybaut 2020.

        :param numpy.ndarray xdata: 1D-NumPy array (numpy.float64)
        :param numpy.ndarray ydata: 1D-NumPy array (numpy.float64)
        :return: Polyline
        :rtype: QtGui.QPolygonF
        """
        dtype = np.float64
        if not (
            xdata.size == ydata.size == xdata.shape[0] == ydata.shape[0]
            and xdata.dtype == ydata.dtype == dtype
        ):
            raise ValueError("Arguments must be 1D, float64 NumPy arrays with same size")
        size = xdata.size
        polyline = QPolygonF(size)
        if PYSIDE2:  # PySide2 (obviously...)
            address = shiboken2.getCppPointer(polyline.data())[0]
            buffer = (ctypes.c_double * 2 * size).from_address(address)
        else:  # PyQt4, PyQt5
            buffer = polyline.data()
            buffer.setsize(2 * size * np.finfo(dtype).dtype.itemsize)
        memory = np.frombuffer(buffer, dtype)
        memory[: (size - 1) * 2 + 1 : 2] = xdata
        memory[1 : (size - 1) * 2 + 2 : 2] = ydata
        return polyline

    def series_to_polyline(xMap, yMap, series, from_, to):
        """
        Convert series data to QPolygon(F) polyline
        """
        xdata = xMap.transform(series.xData()[from_ : to + 1])
        ydata = yMap.transform(series.yData()[from_ : to + 1])
        return array2d_to_qpolygonf(xdata, ydata)

    class QwtPlotCurve_PrivateData(QwtPlotItem_PrivateData):
        def __init__(self):
            QwtPlotItem_PrivateData.__init__(self)
            self.style = QwtPlotCurve.Lines
            self.baseline = 0.0
            self.symbol = None
            self.attributes = 0
            self.legendAttributes = QwtPlotCurve.LegendShowLine
            self.pen = QPen(Qt.black)
            self.brush = QBrush()

    class QwtPlotCurve(QwtPlotSeriesItem, QwtSeriesStore):
        """
        A plot item, that represents a series of points

        A curve is the representation of a series of points in the x-y plane.
        It supports different display styles and symbols.

        .. seealso::

            :py:class:`qwt.symbol.QwtSymbol()`,
            :py:class:`qwt.scale_map.QwtScaleMap()`

        Curve styles:

          * `QwtPlotCurve.NoCurve`:

            Don't draw a curve. Note: This doesn't affect the symbols.

          * `QwtPlotCurve.Lines`:

            Connect the points with straight lines.

          * `QwtPlotCurve.Sticks`:

            Draw vertical or horizontal sticks ( depending on the
            orientation() ) from a baseline which is defined by setBaseline().

          * `QwtPlotCurve.Steps`:

            Connect the points with a step function. The step function
            is drawn from the left to the right or vice versa,
            depending on the QwtPlotCurve::Inverted attribute.

          * `QwtPlotCurve.Dots`:

            Draw dots at the locations of the data points. Note:
            This is different from a dotted line (see setPen()), and faster
            as a curve in QwtPlotCurve::NoStyle style and a symbol
            painting a point.

          * `QwtPlotCurve.UserCurve`:

            Styles >= QwtPlotCurve.UserCurve are reserved for derived
            classes of QwtPlotCurve that overload drawCurve() with
            additional application specific curve types.

        Curve attributes:

          * `QwtPlotCurve.Inverted`:

            For `QwtPlotCurve.Steps` only.
            Draws a step function from the right to the left.

        Legend attributes:

          * `QwtPlotCurve.LegendNoAttribute`:

            `QwtPlotCurve` tries to find a color representing the curve
            and paints a rectangle with it.

          * `QwtPlotCurve.LegendShowLine`:

            If the style() is not `QwtPlotCurve.NoCurve` a line
            is painted with the curve pen().

          * `QwtPlotCurve.LegendShowSymbol`:

            If the curve has a valid symbol it is painted.

          * `QwtPlotCurve.LegendShowBrush`:

            If the curve has a brush a rectangle filled with the
            curve brush() is painted.

        .. py:class:: QwtPlotCurve([title=None])

            Constructor

            :param title: Curve title
            :type title: qwt.text.QwtText or str or None
        """

        # enum CurveStyle
        NoCurve = -1
        Lines, Sticks, Steps, Dots = list(range(4))
        UserCurve = 100

        # enum CurveAttribute
        Inverted = 0x01

        # enum LegendAttribute
        LegendNoAttribute = 0x00
        LegendShowLine = 0x01
        LegendShowSymbol = 0x02
        LegendShowBrush = 0x04

        def __init__(self, title=None):
            if title is None:
                title = QwtText("")
            if not isinstance(title, QwtText):
                title = QwtText(title)
            self.__data = None
            QwtPlotSeriesItem.__init__(self, title)
            QwtSeriesStore.__init__(self)
            self.init()

        @classmethod
        def make(
            cls,
            xdata=None,
            ydata=None,
            title=None,
            plot=None,
            z=None,
            x_axis=None,
            y_axis=None,
            style=None,
            symbol=None,
            linecolor=None,
            linewidth=None,
            linestyle=None,
            antialiased=False,
            size=None,
            finite=None,
        ):
            """
            Create and setup a new `QwtPlotCurve` object (convenience function).

            :param xdata: List/array of x values
            :param ydata: List/array of y values
            :param title: Curve title
            :type title: qwt.text.QwtText or str or None
            :param plot: Plot to attach the curve to
            :type plot: qwt.plot.QwtPlot or None
            :param z: Z-value
            :type z: float or None
            :param x_axis: curve X-axis (default: QwtPlot.yLeft)
            :type x_axis: int or None
            :param y_axis: curve Y-axis (default: QwtPlot.xBottom)
            :type y_axis: int or None
            :param style: curve style (`QwtPlotCurve.NoCurve`, `QwtPlotCurve.Lines`, `QwtPlotCurve.Sticks`, `QwtPlotCurve.Steps`, `QwtPlotCurve.Dots`, `QwtPlotCurve.UserCurve`)
            :type style: int or None
            :param symbol: curve symbol
            :type symbol: qwt.symbol.QwtSymbol or None
            :param linecolor: curve line color
            :type linecolor: QColor or str or None
            :param linewidth: curve line width
            :type linewidth: float or None
            :param linestyle: curve pen style
            :type linestyle: Qt.PenStyle or None
            :param bool antialiased: if True, enable antialiasing rendering
            :param size: size of xData and yData
            :type size: int or None
            :param bool finite: if True, keep only finite array elements (remove all infinity and not a number values), otherwise do not filter array elements

            .. seealso::

                :py:meth:`setData()`, :py:meth:`setPen()`, :py:meth:`attach()`
            """
            item = cls(title)
            if z is not None:
                item.setZ(z)
            if xdata is not None or ydata is not None:
                if xdata is None:
                    raise ValueError("Missing xdata parameter")
                if ydata is None:
                    raise ValueError("Missing ydata parameter")
                item.setData(xdata, ydata, size=size, finite=finite)
            x_axis = QwtPlot.xBottom if x_axis is None else x_axis
            y_axis = QwtPlot.yLeft if y_axis is None else y_axis
            item.setAxes(x_axis, y_axis)
            if style is not None:
                item.setStyle(style)
            if symbol is not None:
                item.setSymbol(symbol)
            linecolor = qcolor_from_str(linecolor, Qt.black)
            linewidth = 1.0 if linewidth is None else linewidth
            linestyle = Qt.SolidLine if linestyle is None else linestyle
            item.setPen(QPen(linecolor, linewidth, linestyle))
            item.setRenderHint(cls.RenderAntialiased, antialiased)
            if plot is not None:
                item.attach(plot)
            return item

        def init(self):
            """Initialize internal members"""
            self.__data = QwtPlotCurve_PrivateData()
            self.setItemAttribute(QwtPlotItem.Legend)
            self.setItemAttribute(QwtPlotItem.AutoScale)
            self.setData(QwtPointArrayData())
            self.setZ(20.0)

        def rtti(self):
            """:return: `QwtPlotItem.Rtti_PlotCurve`"""
            return QwtPlotItem.Rtti_PlotCurve

        def setLegendAttribute(self, attribute, on=True):
            """
            Specify an attribute how to draw the legend icon

            Legend attributes:

                * `QwtPlotCurve.LegendNoAttribute`
                * `QwtPlotCurve.LegendShowLine`
                * `QwtPlotCurve.LegendShowSymbol`
                * `QwtPlotCurve.LegendShowBrush`

            :param int attribute: Legend attribute
            :param bool on: On/Off

            .. seealso::

                :py:meth:`testLegendAttribute()`, :py:meth:`legendIcon()`
            """
            if on != self.testLegendAttribute(attribute):
                if on:
                    self.__data.legendAttributes |= attribute
                else:
                    self.__data.legendAttributes &= ~attribute
                qwtUpdateLegendIconSize(self)
                self.legendChanged()

        def testLegendAttribute(self, attribute):
            """
            :param int attribute: Legend attribute
            :return: True, when attribute is enabled

            .. seealso::

                :py:meth:`setLegendAttribute()`
            """
            return self.__data.legendAttributes & attribute

        def setStyle(self, style):
            """
            Set the curve's drawing style

            Valid curve styles:

                * `QwtPlotCurve.NoCurve`
                * `QwtPlotCurve.Lines`
                * `QwtPlotCurve.Sticks`
                * `QwtPlotCurve.Steps`
                * `QwtPlotCurve.Dots`
                * `QwtPlotCurve.UserCurve`

            :param int style: Curve style

            .. seealso::

                :py:meth:`style()`
            """
            if style != self.__data.style:
                self.__data.style = style
                self.legendChanged()
                self.itemChanged()

        def style(self):
            """
            :return: Style of the curve

            .. seealso::

                :py:meth:`setStyle()`
            """
            return self.__data.style

        def setSymbol(self, symbol):
            """
            Assign a symbol

            The curve will take the ownership of the symbol, hence the previously
            set symbol will be delete by setting a new one. If symbol is None no
            symbol will be drawn.

            :param qwt.symbol.QwtSymbol symbol: Symbol

            .. seealso::

                :py:meth:`symbol()`
            """
            if symbol != self.__data.symbol:
                self.__data.symbol = symbol
                qwtUpdateLegendIconSize(self)
                self.legendChanged()
                self.itemChanged()

        def symbol(self):
            """
            :return: Current symbol or None, when no symbol has been assigned

            .. seealso::

                :py:meth:`setSymbol()`
            """
            return self.__data.symbol

        def setPen(self, *args):
            """
            Build and/or assign a pen, depending on the arguments.

            .. py:method:: setPen(color, width, style)
                :noindex:

                Build and assign a pen

                In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
                non cosmetic (see `QPen.isCosmetic()`). This method signature has
                been introduced to hide this incompatibility.

                :param QColor color: Pen color
                :param float width: Pen width
                :param Qt.PenStyle style: Pen style

            .. py:method:: setPen(pen)
                :noindex:

                Assign a pen

                :param QPen pen: New pen

            .. seealso::

                :py:meth:`pen()`, :py:meth:`brush()`
            """
            if len(args) == 3:
                color, width, style = args
                pen = QPen(color, width, style)
            elif len(args) == 1:
                (pen,) = args
            else:
                raise TypeError(
                    "%s().setPen() takes 1 or 3 argument(s) (%s given)"
                    % (self.__class__.__name__, len(args))
                )
            if pen != self.__data.pen:
                if isinstance(pen, QColor):
                    pen = QPen(pen)
                else:
                    assert isinstance(pen, QPen)
                self.__data.pen = pen
                self.legendChanged()
                self.itemChanged()

        def pen(self):
            """
            :return: Pen used to draw the lines

            .. seealso::

                :py:meth:`setPen()`, :py:meth:`brush()`
            """
            return self.__data.pen

        def setBrush(self, brush):
            """
            Assign a brush.

            In case of `brush.style() != QBrush.NoBrush`
            and `style() != QwtPlotCurve.Sticks`
            the area between the curve and the baseline will be filled.

            In case `not brush.color().isValid()` the area will be filled by
            `pen.color()`. The fill algorithm simply connects the first and the
            last curve point to the baseline. So the curve data has to be sorted
            (ascending or descending).

            :param brush: New brush
            :type brush: QBrush or QColor

            .. seealso::

                :py:meth:`brush()`, :py:meth:`setBaseline()`, :py:meth:`baseline()`
            """
            if isinstance(brush, QColor):
                brush = QBrush(brush)
            else:
                assert isinstance(brush, QBrush)
            if brush != self.__data.brush:
                self.__data.brush = brush
                self.legendChanged()
                self.itemChanged()

        def brush(self):
            """
            :return: Brush used to fill the area between lines and the baseline

            .. seealso::

                :py:meth:`setBrush()`, :py:meth:`setBaseline()`,
                :py:meth:`baseline()`
            """
            return self.__data.brush

        def directPaint(self, from_, to):
            """
            When observing a measurement while it is running, new points have
            to be added to an existing seriesItem. This method can be used to
            display them avoiding a complete redraw of the canvas.

            Setting `plot().canvas().setAttribute(Qt.WA_PaintOutsidePaintEvent, True)`
            will result in faster painting, if the paint engine of the canvas
            widget supports this feature.

            :param int from_: Index of the first point to be painted
            :param int to: Index of the last point to be painted

            .. seealso::

                :py:meth:`drawSeries()`
            """
            directPainter = QwtPlotDirectPainter(self.plot())
            directPainter.drawSeries(self, from_, to)

        def drawSeries(self, painter, xMap, yMap, canvasRect, from_, to):
            """
            Draw an interval of the curve

            :param QPainter painter: Painter
            :param qwt.scale_map.QwtScaleMap xMap: Maps x-values into pixel coordinates.
            :param qwt.scale_map.QwtScaleMap yMap: Maps y-values into pixel coordinates.
            :param QRectF canvasRect: Contents rectangle of the canvas
            :param int from_: Index of the first point to be painted
            :param int to: Index of the last point to be painted. If to < 0 the curve will be painted to its last point.

            .. seealso::

                :py:meth:`drawCurve()`, :py:meth:`drawSymbols()`
            """
            numSamples = self.dataSize()
            if not painter or numSamples <= 0:
                return
            if to < 0:
                to = numSamples - 1
            if qwtVerifyRange(numSamples, from_, to) > 0:
                painter.save()
                painter.setPen(self.__data.pen)
                self.drawCurve(
                    painter, self.__data.style, xMap, yMap, canvasRect, from_, to
                )
                painter.restore()
                if self.__data.symbol and self.__data.symbol.style() != QwtSymbol.NoSymbol:
                    painter.save()
                    self.drawSymbols(
                        painter, self.__data.symbol, xMap, yMap, canvasRect, from_, to
                    )
                    painter.restore()

        def drawCurve(self, painter, style, xMap, yMap, canvasRect, from_, to):
            """
            Draw the line part (without symbols) of a curve interval.

            :param QPainter painter: Painter
            :param int style: curve style, see `QwtPlotCurve.CurveStyle`
            :param qwt.scale_map.QwtScaleMap xMap: Maps x-values into pixel coordinates.
            :param qwt.scale_map.QwtScaleMap yMap: Maps y-values into pixel coordinates.
            :param QRectF canvasRect: Contents rectangle of the canvas
            :param int from_: Index of the first point to be painted
            :param int to: Index of the last point to be painted. If to < 0 the curve will be painted to its last point.

            .. seealso::

                :py:meth:`draw()`, :py:meth:`drawDots()`, :py:meth:`drawLines()`,
                :py:meth:`drawSteps()`, :py:meth:`drawSticks()`
            """
            if style == self.Lines:
                self.drawLines(painter, xMap, yMap, canvasRect, from_, to)
            elif style == self.Sticks:
                self.drawSticks(painter, xMap, yMap, canvasRect, from_, to)
            elif style == self.Steps:
                self.drawSteps(painter, xMap, yMap, canvasRect, from_, to)
            elif style == self.Dots:
                self.drawDots(painter, xMap, yMap, canvasRect, from_, to)

        def drawLines(self, painter, xMap, yMap, canvasRect, from_, to):
            """
            Draw lines

            :param QPainter painter: Painter
            :param qwt.scale_map.QwtScaleMap xMap: Maps x-values into pixel coordinates.
            :param qwt.scale_map.QwtScaleMap yMap: Maps y-values into pixel coordinates.
            :param QRectF canvasRect: Contents rectangle of the canvas
            :param int from_: Index of the first point to be painted
            :param int to: Index of the last point to be painted. If to < 0 the curve will be painted to its last point.

            .. seealso::

                :py:meth:`draw()`, :py:meth:`drawDots()`,
                :py:meth:`drawSteps()`, :py:meth:`drawSticks()`
            """
            if from_ > to:
                return
            doFill = (
                self.__data.brush.style() != Qt.NoBrush
                and self.__data.brush.color().alpha() > 0
            )
            polyline = series_to_polyline(xMap, yMap, self.data(), from_, to)
            painter.drawPolyline(polyline)
            if doFill:
                self.fillCurve(painter, xMap, yMap, canvasRect, polyline)

        def drawSticks(self, painter, xMap, yMap, canvasRect, from_, to):
            """
            Draw sticks

            :param QPainter painter: Painter
            :param qwt.scale_map.QwtScaleMap xMap: Maps x-values into pixel coordinates.
            :param qwt.scale_map.QwtScaleMap yMap: Maps y-values into pixel coordinates.
            :param QRectF canvasRect: Contents rectangle of the canvas
            :param int from_: Index of the first point to be painted
            :param int to: Index of the last point to be painted. If to < 0 the curve will be painted to its last point.

            .. seealso::

                :py:meth:`draw()`, :py:meth:`drawDots()`,
                :py:meth:`drawSteps()`, :py:meth:`drawLines()`
            """
            painter.save()
            painter.setRenderHint(QPainter.Antialiasing, False)
            x0 = xMap.transform(self.__data.baseline)
            y0 = yMap.transform(self.__data.baseline)
            o = self.orientation()
            series = self.data()
            for i in range(from_, to + 1):
                sample = series.sample(i)
                xi = xMap.transform(sample.x())
                yi = yMap.transform(sample.y())
                if o == Qt.Horizontal:
                    painter.drawLine(xi, y0, xi, yi)
                else:
                    painter.drawLine(x0, yi, xi, yi)
            painter.restore()

        def drawDots(self, painter, xMap, yMap, canvasRect, from_, to):
            """
            Draw dots

            :param QPainter painter: Painter
            :param qwt.scale_map.QwtScaleMap xMap: Maps x-values into pixel coordinates.
            :param qwt.scale_map.QwtScaleMap yMap: Maps y-values into pixel coordinates.
            :param QRectF canvasRect: Contents rectangle of the canvas
            :param int from_: Index of the first point to be painted
            :param int to: Index of the last point to be painted. If to < 0 the curve will be painted to its last point.

            .. seealso::

                :py:meth:`draw()`, :py:meth:`drawSticks()`,
                :py:meth:`drawSteps()`, :py:meth:`drawLines()`
            """
            doFill = (
                self.__data.brush.style() != Qt.NoBrush
                and self.__data.brush.color().alpha() > 0
            )
            polyline = series_to_polyline(xMap, yMap, self.data(), from_, to)
            painter.drawPoints(polyline)
            if doFill:
                self.fillCurve(painter, xMap, yMap, canvasRect, polyline)

        def drawSteps(self, painter, xMap, yMap, canvasRect, from_, to):
            """
            Draw steps

            :param QPainter painter: Painter
            :param qwt.scale_map.QwtScaleMap xMap: Maps x-values into pixel coordinates.
            :param qwt.scale_map.QwtScaleMap yMap: Maps y-values into pixel coordinates.
            :param QRectF canvasRect: Contents rectangle of the canvas
            :param int from_: Index of the first point to be painted
            :param int to: Index of the last point to be painted. If to < 0 the curve will be painted to its last point.

            .. seealso::

                :py:meth:`draw()`, :py:meth:`drawSticks()`,
                :py:meth:`drawDots()`, :py:meth:`drawLines()`
            """
            polygon = QPolygonF(2 * (to - from_) + 1)
            inverted = self.orientation() == Qt.Vertical
            if self.__data.attributes & self.Inverted:
                inverted = not inverted
            series = self.data()
            ip = 0
            for i in range(from_, to + 1):
                sample = series.sample(i)
                xi = xMap.transform(sample.x())
                yi = yMap.transform(sample.y())
                if ip > 0:
                    p0 = polygon[ip - 2]
                    if inverted:
                        polygon[ip - 1] = QPointF(p0.x(), yi)
                    else:
                        polygon[ip - 1] = QPointF(xi, p0.y())
                polygon[ip] = QPointF(xi, yi)
                ip += 2
            painter.drawPolyline(polygon)
            if self.__data.brush.style() != Qt.NoBrush:
                self.fillCurve(painter, xMap, yMap, canvasRect, polygon)

        def setCurveAttribute(self, attribute, on=True):
            """
            Specify an attribute for drawing the curve

            Supported curve attributes:

                * `QwtPlotCurve.Inverted`

            :param int attribute: Curve attribute
            :param bool on: On/Off

            .. seealso::

                :py:meth:`testCurveAttribute()`
            """
            if (self.__data.attributes & attribute) == on:
                return
            if on:
                self.__data.attributes |= attribute
            else:
                self.__data.attributes &= ~attribute
            self.itemChanged()

        def testCurveAttribute(self, attribute):
            """
            :return: True, if attribute is enabled

            .. seealso::

                :py:meth:`setCurveAttribute()`
            """
            return self.__data.attributes & attribute

        def fillCurve(self, painter, xMap, yMap, canvasRect, polygon):
            """
            Fill the area between the curve and the baseline with
            the curve brush

            :param QPainter painter: Painter
            :param qwt.scale_map.QwtScaleMap xMap: Maps x-values into pixel coordinates.
            :param qwt.scale_map.QwtScaleMap yMap: Maps y-values into pixel coordinates.
            :param QRectF canvasRect: Contents rectangle of the canvas
            :param QPolygonF polygon: Polygon - will be modified !

            .. seealso::

                :py:meth:`setBrush()`, :py:meth:`setBaseline()`,
                :py:meth:`setStyle()`
            """
            if self.__data.brush.style() == Qt.NoBrush:
                return
            self.closePolyline(painter, xMap, yMap, polygon)
            if polygon.count() <= 2:
                return
            brush = self.__data.brush
            if not brush.color().isValid():
                brush.setColor(self.__data.pen.color())
            painter.save()
            painter.setPen(Qt.NoPen)
            painter.setBrush(brush)
            painter.drawPolygon(polygon)
            painter.restore()

        def closePolyline(self, painter, xMap, yMap, polygon):
            """
            Complete a polygon to be a closed polygon including the
            area between the original polygon and the baseline.

            :param QPainter painter: Painter
            :param qwt.scale_map.QwtScaleMap xMap: Maps x-values into pixel coordinates.
            :param qwt.scale_map.QwtScaleMap yMap: Maps y-values into pixel coordinates.
            :param QPolygonF polygon: Polygon to be completed
            """
            if polygon.size() < 2:
                return
            baseline = self.__data.baseline
            if self.orientation() == Qt.Horizontal:
                if yMap.transformation():
                    baseline = yMap.transformation().bounded(baseline)
                refY = yMap.transform(baseline)
                polygon += QPointF(polygon.last().x(), refY)
                polygon += QPointF(polygon.first().x(), refY)
            else:
                if xMap.transformation():
                    baseline = xMap.transformation().bounded(baseline)
                refX = xMap.transform(baseline)
                polygon += QPointF(refX, polygon.last().y())
                polygon += QPointF(refX, polygon.first().y())

        def drawSymbols(self, painter, symbol, xMap, yMap, canvasRect, from_, to):
            """
            Draw symbols

            :param QPainter painter: Painter
            :param qwt.symbol.QwtSymbol symbol: Curve symbol
            :param qwt.scale_map.QwtScaleMap xMap: Maps x-values into pixel coordinates.
            :param qwt.scale_map.QwtScaleMap yMap: Maps y-values into pixel coordinates.
            :param QRectF canvasRect: Contents rectangle of the canvas
            :param int from_: Index of the first point to be painted
            :param int to: Index of the last point to be painted. If to < 0 the curve will be painted to its last point.

            .. seealso::

                :py:meth:`setSymbol()`, :py:meth:`drawSeries()`,
                :py:meth:`drawCurve()`
            """
            chunkSize = 500
            for i in range(from_, to + 1, chunkSize):
                n = min([chunkSize, to - i + 1])
                points = series_to_polyline(xMap, yMap, self.data(), i, i + n - 1)
                if points.size() > 0:
                    symbol.drawSymbols(painter, points)

        def setBaseline(self, value):
            """
            Set the value of the baseline

            The baseline is needed for filling the curve with a brush or
            the Sticks drawing style.

            The interpretation of the baseline depends on the `orientation()`.
            With `Qt.Horizontal`, the baseline is interpreted as a horizontal line
            at y = baseline(), with `Qt.Vertical`, it is interpreted as a vertical
            line at x = baseline().

            The default value is 0.0.

            :param float value: Value of the baseline

            .. seealso::

                :py:meth:`baseline()`, :py:meth:`setBrush()`,
                :py:meth:`setStyle()`
            """
            if self.__data.baseline != value:
                self.__data.baseline = value
                self.itemChanged()

        def baseline(self):
            """
            :return: Value of the baseline

            .. seealso::

                :py:meth:`setBaseline()`
            """
            return self.__data.baseline

        def closestPoint(self, pos):
            """
            Find the closest curve point for a specific position

            :param QPoint pos: Position, where to look for the closest curve point
            :return: tuple `(index, dist)`

            `dist` is the distance between the position and the closest curve
            point. `index` is the index of the closest curve point, or -1 if
            none can be found ( f.e when the curve has no points ).

            .. note::

                `closestPoint()` implements a dumb algorithm, that iterates
                over all points
            """
            numSamples = self.dataSize()
            if self.plot() is None or numSamples <= 0:
                return -1
            series = self.data()
            xMap = self.plot().canvasMap(self.xAxis())
            yMap = self.plot().canvasMap(self.yAxis())
            index = -1
            dmin = 1.0e10
            for i in range(numSamples):
                sample = series.sample(i)
                cx = xMap.transform(sample.x()) - pos.x()
                cy = yMap.transform(sample.y()) - pos.y()
                f = qwtSqr(cx) + qwtSqr(cy)
                if f < dmin:
                    index = i
                    dmin = f
            dist = np.sqrt(dmin)
            return index, dist

        def legendIcon(self, index, size):
            """
            :param int index: Index of the legend entry (ignored as there is only one)
            :param QSizeF size: Icon size
            :return: Icon representing the curve on the legend

            .. seealso::

                :py:meth:`qwt.plot.QwtPlotItem.setLegendIconSize()`,
                :py:meth:`qwt.plot.QwtPlotItem.legendData()`
            """
            if size.isEmpty():
                return QwtGraphic()
            graphic = QwtGraphic()
            graphic.setDefaultSize(size)
            graphic.setRenderHint(QwtGraphic.RenderPensUnscaled, True)
            painter = QPainter(graphic)
            painter.setRenderHint(
                QPainter.Antialiasing, self.testRenderHint(QwtPlotItem.RenderAntialiased)
            )
            if self.__data.legendAttributes == 0 or (
                self.__data.legendAttributes & QwtPlotCurve.LegendShowBrush
            ):
                brush = self.__data.brush
                if brush.style() == Qt.NoBrush and self.__data.legendAttributes == 0:
                    if self.style() != QwtPlotCurve.NoCurve:
                        brush = QBrush(self.pen().color())
                    elif (
                        self.__data.symbol
                        and self.__data.symbol.style() != QwtSymbol.NoSymbol
                    ):
                        brush = QBrush(self.__data.symbol.pen().color())
                if brush.style() != Qt.NoBrush:
                    r = QRectF(0, 0, size.width(), size.height())
                    painter.fillRect(r, brush)
            if self.__data.legendAttributes & QwtPlotCurve.LegendShowLine:
                if self.pen() != Qt.NoPen:
                    pn = self.pen()
                    #                pn.setCapStyle(Qt.FlatCap)
                    painter.setPen(pn)
                    y = 0.5 * size.height()
                    painter.drawLine(0.0, y, size.width(), y)
            if self.__data.legendAttributes & QwtPlotCurve.LegendShowSymbol:
                if self.__data.symbol:
                    r = QRectF(0, 0, size.width(), size.height())
                    self.__data.symbol.drawSymbol(painter, r)
            return graphic

        def setData(self, *args, **kwargs):
            """
            Initialize data with a series data object or an array of points.

            .. py:method:: setData(data):

                :param data: Series data (e.g. `QwtPointArrayData` instance)
                :type data: .plot_series.QwtSeriesData

            .. py:method:: setData(xData, yData, [size=None], [finite=True]):

                Initialize data with `x` and `y` arrays.

                This signature was removed in Qwt6 and is temporarily maintained here to ensure compatibility with Qwt5.

                Same as `setSamples(x, y, [size=None], [finite=True])`

                :param x: List/array of x values
                :param y: List/array of y values
                :param size: size of xData and yData
                :type size: int or None
                :param bool finite: if True, keep only finite array elements (remove all infinity and not a number values), otherwise do not filter array elements

            .. seealso::

                :py:meth:`setSamples()`
            """
            if len(args) == 1 and not kwargs:
                super(QwtPlotCurve, self).setData(*args)
            elif len(args) in (2, 3, 4):
                self.setSamples(*args, **kwargs)
            else:
                raise TypeError(
                    "%s().setData() takes 1, 2, 3 or 4 argument(s) (%s given)"
                    % (self.__class__.__name__, len(args))
                )

        def setSamples(self, *args, **kwargs):
            """
            Initialize data with an array of points.

            .. py:method:: setSamples(data):

                :param data: Series data (e.g. `QwtPointArrayData` instance)
                :type data: .plot_series.QwtSeriesData

            .. py:method:: setSamples(samples):

                Same as `setData(QwtPointArrayData(samples))`

                :param samples: List/array of points

            .. py:method:: setSamples(xData, yData, [size=None], [finite=True]):

                Same as `setData(QwtPointArrayData(xData, yData, [size=None]))`

                :param xData: List/array of x values
                :param yData: List/array of y values
                :param size: size of xData and yData
                :type size: int or None
                :param bool finite: if True, keep only finite array elements (remove all infinity and not a number values), otherwise do not filter array elements

            .. seealso::

                :py:class:`.plot_series.QwtPointArrayData`
            """
            if len(args) == 1 and not kwargs:
                (samples,) = args
                if isinstance(samples, QwtSeriesData):
                    self.setData(samples)
                else:
                    self.setData(QwtPointArrayData(samples))
            elif len(args) >= 2:
                xData, yData = args[:2]
                try:
                    size = kwargs.pop("size")
                except KeyError:
                    size = None
                try:
                    finite = kwargs.pop("finite")
                except KeyError:
                    finite = None
                if kwargs:
                    raise TypeError(
                        "%s().setSamples(): unknown %s keyword "
                        "argument(s)"
                        % (self.__class__.__name__, ", ".join(list(kwargs.keys())))
                    )
                for arg in args[2:]:
                    if isinstance(arg, bool):
                        finite = arg
                    elif isinstance(arg, int):
                        size = arg
                self.setData(QwtPointArrayData(xData, yData, size=size, finite=finite))
            else:
                raise TypeError(
                    "%s().setSamples() takes 1, 2 or 3 argument(s) "
                    "(%s given)" % (self.__class__.__name__, len(args))
                )