from ..constant import *
from ..exception import *
from ..session import Style
from ..tecutil import Index, sv, _tecutil, color_spec
from collections import namedtuple
from ..tecutil import ArgList, lock, IndexSet
from .scatter import Symbol, TextSymbol, GeometrySymbol
import ctypes
from builtins import int
from ..version import sdk_version_info


class StreamtraceStyle(Style):
    def __init__(self, streamtrace, *sv_args):
        self.streamtrace = streamtrace
        kw = dict(uniqueid=streamtrace.plot.frame.uid)
        super().__init__(streamtrace._sv, *sv_args, **kw)

_XYZ = namedtuple('XYZ', ['x', 'y', 'z'])
_XYZ.__new__.__defaults__ = (None, None, None)


class StreamtraceTiming(StreamtraceStyle):
    """Timed markers for streamlines.

    Use `StreamtraceTiming` to control timed markers for streamlines, and timed
    dashes for all types of streamtraces. Stream markers are drawn at time
    locations along streamlines. The spacing between stream markers is
    proportional to the magnitude of the local vector field.

    .. code-block:: python
        :emphasize-lines: 19-24

        import tecplot
        from tecplot.constant import *
        import os

        examples_dir = tecplot.session.tecplot_examples_directory()
        datafile = os.path.join(examples_dir, '2D', 'cylinder.plt')
        dataset = tecplot.data.load_tecplot(datafile)

        frame = tecplot.active_frame()
        frame.plot_type = tecplot.constant.PlotType.Cartesian2D

        plot = frame.plot()
        plot.vector.u_variable_index = 3
        plot.vector.v_variable_index = 4
        plot.show_streamtraces = True

        streamtraces = plot.streamtraces
        streamtraces.show_markers = True
        timing = streamtraces.timing
        timing.anchor = 0
        timing.start = 0.01
        timing.end = 0.2
        timing.delta = 0.1

        streamtraces.add(seed_point=[1, 1], stream_type=Streamtrace.TwoDLine)

        tecplot.export.save_png('streamtrace_timing.png', 600)

    .. figure:: /_static/images/streamtrace_timing.png
        :width: 300px
        :figwidth: 300px

    """

    def __init__(self, streamtrace):
        super().__init__(streamtrace, sv.STREAMTIMING)

    @lock()
    def reset_delta(self):
        """Reset the time delta for dashed streamtraces.


        The delta time is reset such that a stream dash in the vicinity of
        the maximum vector magnitude will have a length approximately equal
        to 10 percent of the frame width.

        Raises:
            `TecplotSystemError`: Streamtraces time delta could not be reset.

        Example usage::

            >>> plot.streamtraces.timing.reset_delta()
        """
        with self.streamtrace.plot.frame.activated():
            if not _tecutil.StreamtraceResetDelta():
                raise TecplotSystemError()

    @property
    def _show_dashes(self):
        return self._get_style(bool, sv.SHOWDASHES)

    @_show_dashes.setter
    def _show_dashes(self, value):
        self._set_style(bool(value), sv.SHOWDASHES)

    @property
    def _show_markers(self):
        return self._get_style(bool, sv.SHOWMARKERS)

    @_show_markers.setter
    def _show_markers(self, value):
        self._set_style(bool(value), sv.SHOWMARKERS)

    @property
    def _marker_color(self):
        return color_spec(self._get_style(Color, sv.MARKCOLOR),
                          self.streamtrace.plot)

    @_marker_color.setter
    def _marker_color(self, value):
        self._set_style(color_spec(value), sv.MARKCOLOR)

    @property
    def _marker_size(self):
        return self._get_style(float, sv.MARKSIZE)

    @_marker_size.setter
    def _marker_size(self, value):
        self._set_style(float(value), sv.MARKSIZE)

    @property
    def _dash_skip(self):
        return self._get_style(int, sv.DASHSKIP)

    @_dash_skip.setter
    def _dash_skip(self, value):
        self._set_style(int(value), sv.DASHSKIP)

    @property
    def _marker_symbol_type(self):
        return Symbol(self, sv.MARKSYMBOL)._symbol_type

    @_marker_symbol_type.setter
    def _marker_symbol_type(self, value):
        Symbol(self, sv.MARKSYMBOL)._symbol_type = value

    def _marker_symbol(self, symbol_type=None):
        _dispatch = {
            SymbolType.Text: TextSymbol,
            SymbolType.Geometry: GeometrySymbol}
        return _dispatch[symbol_type or self._marker_symbol_type](
            self, sv.MARKSYMBOL)

    @property
    def start(self):
        """Time at which the first marker should be drawn.

        :type: `float`

        A start time of zero means that the first marker is drawn at the
        starting point. A start time of 2.5 means that the first stream
        marker is drawn 2.5 time units downstream of the starting point.

        Example usage::

            >>> plot.streamtraces.timing.start = 2.5
        """
        return self._get_style(float, sv.TIMESTART)

    @start.setter
    def start(self, value):
        self._set_style(float(value), sv.TIMESTART)

    @property
    def end(self):
        """Time after which no stream markers are drawn.

        :type: `float`

        Example usage::

            >>> plot.streamtraces.timing.end = 3.0
        """
        return self._get_style(float, sv.TIMEEND)

    @end.setter
    def end(self, value):
        self._set_style(float(value), sv.TIMEEND)

    @property
    def anchor(self):
        """Time that a dash is guaranteed to start.

        :type: `float`

        A dash is guaranteed to start at `anchor`, provided the start
        and end time surround the dash.

        Example usage::

            >>> plot.streamtraces.timing.anchor = 1.1
        """
        return self._get_style(float, sv.TIMEANCHOR)

    @anchor.setter
    def anchor(self, value):
        self._set_style(float(value), sv.TIMEANCHOR)

    @property
    def delta(self):
        """Time between stream markers.

        :type: `float`

        `delta` is the time interval that measures the time between
        stream markers. The actual distance between markers is the product
        of this number and the local `Vector` magnitude.

        Call `StreamtraceTiming.reset_delta()` to reset this to the default.

        Example usage::

            >>> plot.streamtraces.timing.delta = 0.1
        """
        return self._get_style(float, sv.TIMEDELTA)

    @delta.setter
    def delta(self, value):
        self._set_style(float(value), sv.TIMEDELTA)


class StreamtraceTerminationLine(StreamtraceStyle):
    """Streamtraces termination line attributes.

    A streamtrace termination line is a polyline that terminates any
    streamtraces that cross it. The termination line is useful for stopping
    streamtraces before they spiral or stall.

    .. note::
        Before setting any `StreamtraceTerminationLine` properties, you must
        `add a termination line <set_termination_line>`.

    Streamtraces are terminated whenever any of the following occur:

        * The maximum number of integration steps is reached.
        * Any point where a streamtrace passes outside the available data.
        * The streamtrace reaches a point where the velocity magnitude is zero.

    .. code-block:: python
        :emphasize-lines: 24-34

        import os
        import tecplot
        from tecplot.constant import *
        from tecplot.plot import Cartesian3DFieldPlot

        examples_dir = tecplot.session.tecplot_examples_directory()
        datafile = os.path.join(examples_dir, '3D_Volume', 'eddy.plt')
        dataset = tecplot.data.load_tecplot(datafile)

        frame = tecplot.active_frame()
        frame.plot_type = tecplot.constant.PlotType.Cartesian3D

        plot = frame.plot()  # type: Cartesian3DFieldPlot
        plot.fieldmap(0).surfaces.surfaces_to_plot = SurfacesToPlot.BoundaryFaces
        plot.show_mesh = True
        plot.show_shade = False

        plot.vector.u_variable_index = 4
        plot.vector.v_variable_index = 5
        plot.vector.w_variable_index = 6
        plot.show_streamtraces = True

        streamtraces = plot.streamtraces
        streamtraces.set_termination_line([(18.2293706294, 50.5561212121),
                                        (51.7714125874, -19.9307785548)])

        term_line = streamtraces.termination_line
        term_line.is_active = True
        term_line.show = True
        term_line.color = Color.Red
        term_line.line_pattern = LinePattern.Dashed
        term_line.pattern_length = 3
        term_line.line_thickness = .5

        plot.streamtraces.add_rake(
            start_position=(45.49, 15.32, 59.1),
            end_position=(48.89, 53.2, 47.6),
            stream_type=Streamtrace.VolumeLine)

        tecplot.export.save_png('streamtrace_term_line.png', 600)

    .. figure:: /_static/images/streamtrace_term_line.png
        :width: 300px
        :figwidth: 300px
    """

    def __init__(self, streamtrace):
        super().__init__(streamtrace, sv.TERMLINE)

    @property
    def is_active(self):
        """Activate/disable the streamtrace termination line.

        :type: `boolean <bool>`

        Set to `True` to activate the termination line and terminate any
        streamtraces that cross it. Set to `False` and redraw the plot
        with unterminated streamtraces.

        .. note::
            To display the termination line itself, set `show` to `True`.

        Example usage::

            >>> plot.streamtraces.termination_line.is_active = True
        """
        return self._get_style(bool, sv.ISACTIVE)

    @is_active.setter
    def is_active(self, value):
        self._set_style(bool(value), sv.ISACTIVE)

    @property
    def show(self):
        """Display the termination line.

        :type: `boolean <bool>`

        Set to `True` to display the termination line. Set to `False` and
        redraw the plot to display terminated streamlines (if `is_active` is
        set to `True`), but not the termination line itself.

        .. note::
            To display terminated streamtraces, `is_active` must be set to
            `True`.

        Example usage::

            >>> plot.streamtraces.termination_line.show = True
        """
        return self._get_style(bool, sv.SHOW)

    @show.setter
    def show(self, value):
        self._set_style(bool(value), sv.SHOW)

    @property
    def color(self):
        """Color of the termination line.

        :type: `Color`

        Example usage::

            >>> plot.streamtraces.termination_line.color = Color.Red
        """
        return self._get_style(Color, sv.COLOR)

    @color.setter
    def color(self, value):
        self._set_style(Color(value), sv.COLOR)

    @property
    def line_pattern(self):
        """Pattern of the terminating line.

        :type: `LinePattern`

        Example usage::

            >>> plot.streamtraces.termination_line.line_pattern = LinePattern.Dotted
        """
        return self._get_style(LinePattern, sv.LINEPATTERN)

    @line_pattern.setter
    def line_pattern(self, value):
        self._set_style(LinePattern(value), sv.LINEPATTERN)

    @property
    def pattern_length(self):
        """Length of the pattern as a percentage of frame height.

        :type: `float`

        Example usage::

            >>> plot.streamtraces.termination_line.pattern_length = 2
        """
        return self._get_style(float, sv.PATTERNLENGTH)

    @pattern_length.setter
    def pattern_length(self, value):
        self._set_style(float(value), sv.PATTERNLENGTH)

    @property
    def line_thickness(self):
        """Thickness of the termination line as a percentage of frame height.

        :type: `float`

        Example usage::

            >>> plot.streamtraces.termination_line.line_thickness = 0.1
        """
        return self._get_style(float, sv.LINETHICKNESS)

    @line_thickness.setter
    def line_thickness(self, value):
        self._set_style(float(value), sv.LINETHICKNESS)


class StreamtraceRodRibbonMesh(StreamtraceStyle):
    """Streamtraces rod/ribbon mesh attributes.

    .. note::
        To set the mesh color or line thickness, see `Streamtraces.color`
        and `Streamtraces.line_thickness`.

    .. code-block:: python
        :emphasize-lines: 25

        import os
        import tecplot
        from tecplot.constant import *
        from tecplot.plot import Cartesian3DFieldPlot

        examples_dir = tecplot.session.tecplot_examples_directory()
        datafile = os.path.join(examples_dir, '3D_Volume', 'jetflow.plt')
        dataset = tecplot.data.load_tecplot(datafile)

        frame = tecplot.active_frame()
        frame.plot_type = tecplot.constant.PlotType.Cartesian3D

        plot = frame.plot()  # type: Cartesian3DFieldPlot
        plot.fieldmap(0).surfaces.surfaces_to_plot = SurfacesToPlot.BoundaryFaces
        plot.show_mesh = False
        plot.show_shade = False

        plot.vector.u_variable_index = 4
        plot.vector.v_variable_index = 5
        plot.vector.w_variable_index = 6
        plot.show_streamtraces = True

        ribbon = plot.streamtraces.rod_ribbon
        ribbon.width = .018
        ribbon.mesh.show = True

        plot.streamtraces.add_rake(
            start_position=(0.323, 0.103, 0.491),
            end_position=(-0.367, 0.229, 0.116),
            stream_type=Streamtrace.VolumeRibbon)

        tecplot.export.save_png('streamtrace_ribbon_mesh.png', 600)

    .. figure:: /_static/images/streamtrace_ribbon_mesh.png
        :width: 300px
        :figwidth: 300px

    """
    def __init__(self, streamtrace):
        super().__init__(streamtrace, sv.MESH)

    @property
    def show(self):
        """Display mesh.

        .. note::

            The mesh color for streamtraces is determined by the line color.

        :type: `boolean <bool>`

        Example usage::

            >>> plot.streamtraces.rod_ribbon.mesh.show = True
        """
        return self._get_style(bool, sv.SHOW)

    @show.setter
    def show(self, value):
        self._set_style(bool(value), sv.SHOW)

    @property
    def line_thickness(self):
        """ Get/Set streamtrace rod/ribbon mesh line thickness as a percentage
        of frame height.

        Typical values are **.02, .1, .4, .8, 1.5**

        :type: `float`

        Example usage::

            >>> plot.streamtraces.rod_ribbon.mesh.line_thickness = 0.2
        """
        return self._get_style(float, sv.LINETHICKNESS)

    @line_thickness.setter
    def line_thickness(self, value):
        self._set_style(float(value), sv.LINETHICKNESS)


class StreamtraceRodRibbonContour(StreamtraceStyle):
    """Contour flooding display for streamtrace rod/ribbons.

    .. code-block:: python
        :emphasize-lines: 27-28

        import os
        import tecplot
        from tecplot.constant import *
        from tecplot.plot import Cartesian3DFieldPlot

        examples_dir = tecplot.session.tecplot_examples_directory()
        datafile = os.path.join(examples_dir, '3D_Volume', 'ductflow.plt')
        dataset = tecplot.data.load_tecplot(datafile)

        frame = tecplot.active_frame()
        frame.plot_type = tecplot.constant.PlotType.Cartesian3D

        plot = frame.plot()  # type: Cartesian3DFieldPlot
        plot.fieldmap(0).surfaces.surfaces_to_plot = SurfacesToPlot.BoundaryFaces
        plot.contour(0).variable = dataset.variable(3)
        plot.contour(0).levels.reset_to_nice()

        plot.show_mesh = False
        plot.show_shade = False

        plot.vector.u_variable_index = 3
        plot.vector.v_variable_index = 4
        plot.vector.w_variable_index = 5
        plot.show_streamtraces = True

        rod_ribbon = plot.streamtraces.rod_ribbon
        rod_ribbon.contour.show = True
        rod_ribbon.contour.use_lighting_effect = True

        plot.streamtraces.add(seed_point=[0, 0, 0],
                              stream_type=Streamtrace.VolumeRibbon)

        plot.streamtraces.rod_ribbon.width = .5

        tecplot.export.save_png('streamtrace_ribbon_contour.png', 600)

    .. figure:: /_static/images/streamtrace_ribbon_contour.png
        :width: 300px
        :figwidth: 300px
    """

    def __init__(self, streamtrace):
        super().__init__(streamtrace, sv.CONTOUR)

    @property
    def use_lighting_effect(self):
        """Enable lighting effect for streamtrace rod/ribbons.

        .. note::
            Setting `StreamtraceRodRibbonContour.use_lighting_effect` will also set
            the same value for `StreamtraceRodRibbonShade.use_lighting_effect`,
            and vice-versa.

        The lighting effect is set with `StreamtraceRodRibbonEffects.lighting_effect`, and may
        be one of `LightingEffect.Gouraud` or `LightingEffect.Paneled`.

        :type: `boolean <bool>`

        Example usage::

            >>> ribbon = plot.streamtraces.rod_ribbon
            >>> contour = ribbon.contour
            >>> contour.use_lighting_effect = True
            >>> ribbon.effects.lighting_effect = LightingEffect.Paneled
        """
        return self._get_style(bool, sv.USELIGHTINGEFFECT)

    @use_lighting_effect.setter
    def use_lighting_effect(self, value):
        self._set_style(bool(value), sv.USELIGHTINGEFFECT)

    @property
    def show(self):
        """Enable or disable contour flooding display.

        :type: `boolean <bool>`

        Example usage::

            >>> plot.streamtraces.rod_ribbon.contour.show = True
        """
        return self._get_style(bool, sv.SHOW)

    @show.setter
    def show(self, value):
        self._set_style(bool(value), sv.SHOW)

    @property
    def flood_contour_group_index(self):
        """The `Index` of the `ContourGroup` to use for flooding.

        :type: `Index` (zero-based index)

        This property sets and gets, by `Index`, the `ContourGroup` used for
        flooding. Changing style on this `ContourGroup` will affect all
        fieldmaps on the same `Frame` that use it.

        Example usage::

            >>> contour = plot.streamtraces.rod_ribbon.contour
            >>> contour.flood_contour_group_index = 0  # First contour group
        """
        return self._get_style(Index, sv.FLOODCOLORING)

    @flood_contour_group_index.setter
    def flood_contour_group_index(self, index):
        self._set_style(Index(index), sv.FLOODCOLORING)

    @property
    def flood_contour_group(self):
        """Contour group to use for flooding.

        :type: `ContourGroup`

        This property sets and gets the ContourGroup used for flooding.
        Changing style on this `ContourGroup` will affect all fieldmaps on
        the same `Frame` that use it.

        Example usage::

            >>> group = plot.contour(1)
            >>> contour = plot.streamtraces.rod_ribbon.contour
            >>> contour.flood_contour_group = group
        """
        return self.streamtrace.plot.contour(self.flood_contour_group_index)

    @flood_contour_group.setter
    def flood_contour_group(self, flood_contour_group):
        self.flood_contour_group_index = flood_contour_group.index


class StreamtraceRodRibbonShade(StreamtraceStyle):
    """Color and lighting display for rod/ribbons.

    .. code-block:: python
        :emphasize-lines: 27-29

        import os
        import tecplot
        from tecplot.constant import *
        from tecplot.plot import Cartesian3DFieldPlot

        examples_dir = tecplot.session.tecplot_examples_directory()
        datafile = os.path.join(examples_dir, '3D_Volume', 'ductflow.plt')
        dataset = tecplot.data.load_tecplot(datafile)

        frame = tecplot.active_frame()
        frame.plot_type = tecplot.constant.PlotType.Cartesian3D

        plot = frame.plot()  # type: Cartesian3DFieldPlot
        plot.fieldmap(0).surfaces.surfaces_to_plot = SurfacesToPlot.BoundaryFaces
        plot.contour(0).variable = dataset.variable(3)
        plot.contour(0).levels.reset_to_nice()

        plot.show_mesh = False
        plot.show_shade = False

        plot.vector.u_variable_index = 3
        plot.vector.v_variable_index = 4
        plot.vector.w_variable_index = 5
        plot.show_streamtraces = True
        plot.streamtraces.show_paths = True

        plot.streamtraces.rod_ribbon.shade.show = True
        plot.streamtraces.rod_ribbon.shade.color = Color.Blue
        plot.streamtraces.rod_ribbon.shade.use_lighting_effect = False

        plot.streamtraces.add_rake(start_position=(0, 0, 0),
                                   end_position=(.05, .05, .175),
                                   stream_type=Streamtrace.VolumeRibbon)

        tecplot.export.save_png('streamtrace_ribbon_shade.png', 600)

    .. figure:: /_static/images/streamtrace_ribbon_shade.png
        :width: 300px
        :figwidth: 300px
    """
    def __init__(self, streamtrace):
        super().__init__(streamtrace, sv.SHADE)

    @property
    def show(self):
        """Show shade attributes.

        :type: `boolean <bool>`

        Example usage::

            >>> plot.streamtraces.rod_ribbon.shade.show = True
        """
        return self._get_style(bool, sv.SHOW)

    @show.setter
    def show(self, value):
        self._set_style(bool(value), sv.SHOW)

    @property
    def color(self):
        """Shade color.

        :type: `Color`

        `Color.MultiColor` and `Color.RGBColor` coloring are not available.
        Use flooded contours for multi-color or RGB flooding.

        Example usage::

            >>> plot.streamtraces.rod_ribbon.shade.show = True
            >>> plot.streamtraces.rod_ribbon.shade.color = Color.Blue
        """
        return self._get_style(Color, sv.COLOR)

    @color.setter
    def color(self, value):
        self._set_style(Color(value), sv.COLOR)

    @property
    def use_lighting_effect(self):
        """Use lighting effect.

        When set to `True`, the lighting effect may be selected with the
        `SliceEffects.lighting_effect` attribute.

        .. note::
            Setting `SliceShade.use_lighting_effect` will also set
            the same value for `SliceContour.use_lighting_effect`,
            and vice-versa.

        :type: `Boolean <bool>`

        Example usage::

            >>> plot.streamtraces.rod_ribbon.shade.use_lighting_effect = True
            >>> plot.streamtraces.rod_ribbon.effects.lighting_effect = LightingEffect.Paneled
        """
        return self._get_style(bool, sv.USELIGHTINGEFFECT)

    @use_lighting_effect.setter
    def use_lighting_effect(self, value):
        self._set_style(bool(value), sv.USELIGHTINGEFFECT)


class StreamtraceRodRibbonEffects(StreamtraceStyle):
    """Controls how lighting and translucency interacts with streamtrace rods and ribbons.

    .. code-block:: python
        :emphasize-lines: 27-29

        import os
        import tecplot
        from tecplot.constant import *
        from tecplot.plot import Cartesian3DFieldPlot

        examples_dir = tecplot.session.tecplot_examples_directory()
        datafile = os.path.join(examples_dir, '3D_Volume', 'ductflow.plt')
        dataset = tecplot.data.load_tecplot(datafile)

        frame = tecplot.active_frame()
        frame.plot_type = tecplot.constant.PlotType.Cartesian3D

        plot = frame.plot()  # type: Cartesian3DFieldPlot
        plot.fieldmap(0).surfaces.surfaces_to_plot = SurfacesToPlot.BoundaryFaces
        plot.contour(0).variable = dataset.variable(3)
        plot.contour(0).levels.reset_to_nice()

        plot.show_mesh = False
        plot.show_shade = False

        plot.vector.u_variable_index = 3
        plot.vector.v_variable_index = 4
        plot.vector.w_variable_index = 5
        plot.show_streamtraces = True
        plot.streamtraces.rod_ribbon.width = .03
        plot.streamtraces.rod_ribbon.shade.color = Color.Green

        plot.streamtraces.rod_ribbon.effects.use_translucency = True
        plot.streamtraces.rod_ribbon.effects.surface_translucency = 80

        plot.streamtraces.add_rake(start_position=(0, 0, 0),
                                   end_position=(.05, .05, .175),
                                   stream_type=Streamtrace.VolumeRibbon)

        tecplot.export.save_png('streamtrace_ribbon_effects.png', 600)

    .. figure:: /_static/images/streamtrace_ribbon_effects.png
        :width: 300px
        :figwidth: 300px
    """

    def __init__(self, streamtrace):
        super().__init__(streamtrace, sv.EFFECTS)

    @property
    def lighting_effect(self):
        """Get/set the lighting algorithm used when lighting
           streamtrace rods and ribbons.

        :type: `LightingEffect`

        Ribbon lighting effects must be enabled by setting
        `StreamtraceRodRibbonShade.use_lighting_effect` to `True` when
        setting this value.

        Note that setting `StreamtraceRodRibbonShade.use_lighting_effect` will also set
        this value for `ribbon contours <StreamtraceRodRibbonContour>`.

        There are two types of lighting effects: Paneled and Gouraud:

            * `Paneled`: Within each cell, the color assigned to each area by
                shading or contour flooding is tinted by a shade constant
                across the cell. This shade is based on the orientation
                of the cell relative to your 3D light source.
            * `Gouraud`: This offers smoother, more continuous shading than
                Paneled shading, but it also results in slower plotting
                and larger print files. `Gouraud` shading is not continuous
                across zone boundaries unless face neighbors are specified
                in the data. `Gouraud` shading is not available for finite
                element volume `Zone <data_access>` when blanking is active.
                The zone's lighting effect reverts to `Paneled`
                shading in this case.

        Example usage::

            >>> plot.streamtraces.rod_ribbon.shade.use_lighting_effect = True
            >>> plot.streamtraces.rod_ribbon.effects.lighting_effect = LightingEffect.Paneled
        """
        return self._get_style(LightingEffect, sv.LIGHTINGEFFECT)

    @lighting_effect.setter
    def lighting_effect(self, value):
        self._set_style(LightingEffect(value), sv.LIGHTINGEFFECT)

    @property
    def surface_translucency(self):
        """Surface translucency of the streamtraces ribbon.

        :type: `integer <int>`

        Surface translucency must be enabled by setting
        `StreamtraceRodRibbonEffects.use_translucency` = `True`
        when setting this value.

        Valid translucency values range from one (opaque) to 99 (translucent).

        Example usage::

            >>> plot.streamtraces.rod_ribbon.effects.use_translucency = True
            >>> plot.streamtraces.rod_ribbon.effects.surface_translucency = 20
        """
        return self._get_style(int, sv.SURFACETRANSLUCENCY)

    @surface_translucency.setter
    def surface_translucency(self, value):
        self._set_style(int(value), sv.SURFACETRANSLUCENCY)

    @property
    def use_translucency(self):
        """Enable surface translucency.

        :type: `boolean <bool>`

        The surface translucency value can be changed by setting
        `StreamtraceRodRibbonEffects.surface_translucency`.

        Example usage::

            >>> plot.streamtraces.rod_ribbon.effects.use_translucency = True
            >>> plot.streamtraces.rod_ribbon.effects.surface_translucency = 20
        """
        return self._get_style(bool, sv.USETRANSLUCENCY)

    @use_translucency.setter
    def use_translucency(self, value):
        self._set_style(bool(value), sv.USETRANSLUCENCY)


class StreamtraceRodRibbon(StreamtraceStyle):
    """Get/Set streamtrace rod/ribbon attributes.

    The `StreamtraceRodRibbon` class allows you to query and set attributes
    of streamtrace rod/ribbon types:

    * `Streamtrace.SurfaceRibbon`
    * `Streamtrace.VolumeRibbon`
    * `Streamtrace.VolumeRod`

    In addition to attributes common to all rod/ribbon
    streamtrace types such as `width <StreamtraceRodRibbon.width>`,
    some attributes are further divided into subcategories:

    * `Rod/ribbon mesh <StreamtraceRodRibbonMesh>`
    * `Rod/ribbon contour <StreamtraceRodRibbonContour>`
    * `Rod/ribbon shade <StreamtraceRodRibbonShade>`
    * `Rod/ribbon effects <StreamtraceRodRibbonEffects>`

    .. note::
        To change the color of streamtrace rods/ribbons, set
        `StreamtraceRodRibbonShade.color`.

    .. code-block:: python
        :emphasize-lines: 26-28

        import os
        import tecplot
        from tecplot.constant import *
        from tecplot.plot import Cartesian3DFieldPlot

        examples_dir = tecplot.session.tecplot_examples_directory()
        datafile = os.path.join(examples_dir, '3D_Volume', 'ductflow.plt')
        dataset = tecplot.data.load_tecplot(datafile)

        frame = tecplot.active_frame()
        frame.plot_type = tecplot.constant.PlotType.Cartesian3D

        plot = frame.plot()  # type: Cartesian3DFieldPlot
        plot.fieldmap(0).surfaces.surfaces_to_plot = SurfacesToPlot.BoundaryFaces
        plot.contour(0).variable = dataset.variable(3)
        plot.contour(0).levels.reset_to_nice()

        plot.show_mesh = True
        plot.show_shade = False

        plot.vector.u_variable_index = 3
        plot.vector.v_variable_index = 4
        plot.vector.w_variable_index = 5

        plot.show_streamtraces = True
        plot.streamtraces.rod_ribbon.width = .05
        plot.streamtraces.rod_ribbon.contour.show = True

        plot.streamtraces.add_rake(start_position=(0, 0, 0),
                                   end_position=(.05, .05, .175),
                                   stream_type=Streamtrace.VolumeRibbon,
                                   num_seed_points=26)

        tecplot.export.save_png('streamtrace_ribbon.png', 600)

    .. figure:: /_static/images/streamtrace_ribbon.png
        :width: 300px
        :figwidth: 300px

    """
    def __init__(self, streamtrace):
        super().__init__(streamtrace, sv.RODRIBBON)
        self.plot = streamtrace.plot

    @property
    def mesh(self):
        """Streamtraces rod/ribbon mesh attributes.

        :type: `StreamtraceRodRibbonMesh`

        Example usage::

            >>> plot.streamtraces.rod_ribbon.mesh.show = True
        """
        return StreamtraceRodRibbonMesh(self)

    @property
    def contour(self):
        """Streamtraces rod/ribbon contour attributes.

        :type: `StreamtraceRodRibbonContour`

        Example usage::

            >>> plot.streamtraces.rod_ribbon.contour.show = True
        """
        return StreamtraceRodRibbonContour(self)

    @property
    def shade(self):
        """Streamtraces rod/ribbon color and lighting attributes.

        :type: `StreamtraceRodRibbonShade`

        Example usage::

            >>> plot.streamtraces.rod_ribbon.shade.color = Color.Magenta
        """
        return StreamtraceRodRibbonShade(self)

    @property
    def effects(self):
        """Streamtraces rod/ribbon effects.

        :type: `StreamtraceRodRibbonEffects`

        Example usage::

            >>> plot.streamtraces.rod_ribbon.effects.use_translucency = True
        """
        return StreamtraceRodRibbonEffects(self)

    @property
    def width(self):
        """Rod/ribbon width in grid units.

        :type: `float`

        Example usage::

            >>> plot.streamtraces.rod_ribbon.width = 0.01
        """
        return self._get_style(float, sv.WIDTH)

    @width.setter
    def width(self, value):
        self._set_style(float(value), sv.WIDTH)

    @property
    def num_rod_points(self):
        """Number of rod points.

        :type: `integer <int>`, valid range 3-100

        Volume rods have a polygonal cross-section; this parameter tells
        |Tecplot 360| what that cross-section should be.
        (Three is an equilateral triangle, four is a square,
        five is a regular pentagon, and so on.) If you want two sets
        of volume rods with different cross-sections, you must create one
        set and then extract the set as a zone, then configure a new
        set of streamtraces with the second cross-section.

        Example usage::

            >>> plot.streamtraces.rod_ribbon.num_rod_points = 10
        """
        return self._get_style(int, sv.NUMRODPOINTS)

    @num_rod_points.setter
    def num_rod_points(self, value):
        self._set_style(int(value), sv.NUMRODPOINTS)


class Streamtraces(Style):
    """Streamtrace attributes for the plot.

    A streamtrace is the path traced by a massless particle placed at an
    arbitrary location in a steady-state vector field.
    Streamtraces may be used to illustrate the nature of the vector
    field flow in a particular region of the `Plot`.

    Note:
        Because streamtraces are dependent upon a vector field, you must
        define vector components before creating streamtraces.
        However, it is not necessary to activate the Vector zone layer to
        use streamtraces.

    .. code-block:: python
        :emphasize-lines: 23-30

        import os
        import tecplot
        from tecplot.constant import *
        from tecplot.plot import Cartesian3DFieldPlot

        examples_dir = tecplot.session.tecplot_examples_directory()
        datafile = os.path.join(examples_dir, '3D_Volume', 'eddy.plt')
        dataset = tecplot.data.load_tecplot(datafile)

        frame = tecplot.active_frame()
        frame.plot_type = tecplot.constant.PlotType.Cartesian3D

        plot = frame.plot()
        plot.fieldmap(0).surfaces.surfaces_to_plot = SurfacesToPlot.BoundaryFaces
        plot.show_mesh = True
        plot.show_shade = False

        plot.vector.u_variable_index = 4
        plot.vector.v_variable_index = 5
        plot.vector.w_variable_index = 6
        plot.show_streamtraces = True

        streamtraces = plot.streamtraces
        streamtraces.color = Color.Blue

        streamtraces.show_arrows = True
        streamtraces.arrowhead_size = 3
        streamtraces.step_size = .25
        streamtraces.line_thickness = .2
        streamtraces.max_steps = 100

        streamtraces.add_rake(start_position=(45.49, 15.32, 59.1),
                              end_position=(48.89, 53.2, 47.6),
                              stream_type=Streamtrace.SurfaceLine)


        tecplot.export.save_png('streamtrace_example.png', 600)

    .. figure:: /_static/images/streamtrace_example.png
        :width: 300px
        :figwidth: 300px
    """

    def __init__(self, plot):
        super().__init__(sv.STREAMATTRIBUTES, uniqueid=plot.frame.uid)
        self.plot = plot

    @property
    def rod_ribbon(self):
        """Streamtrace rod/ribbon attributes.

        :type: `StreamtraceRodRibbon`

        Example usage::
        
            >>> streamtraces.rod_ribbon.mesh.show = True
        """
        return StreamtraceRodRibbon(self)

    @property
    def timing(self):
        """Streamtraces timing attributes.

        :type: `StreamtraceTiming`

        Example usage::

            >>> timing = plot.streamtraces.timing
            >>> timing.start = 0.01
        """
        return StreamtraceTiming(self)

    @property
    def termination_line(self):
        """Streamtraces termination line attributes.

        A streamtrace termination line is a polyline that terminates any
        streamtraces that cross it. The termination line is useful for stopping
        streamtraces before they spiral or stall.

        :type: `StreamtraceTerminationLine`

        Example usage::

            >>> term_line = plot.streamtraces.termination_line
            >>> term_line.show = True
        """
        return StreamtraceTerminationLine(self)

    @property
    def show_dashes(self):
        """Display streamtrace dashes.

        The lengths of the dashes and the spaces between the dashes are
        controlled by the value of `StreamtraceTiming.delta`. Set the
        `Streamtraces.dash_skip` attribute to control the number of
        time deltas used for the "off" sections of the streamtraces.

        :type: `boolean <bool>`

        Example usage::

            >>> plot.streamtraces.show_dashes = True
        """
        return self.timing._show_dashes

    @show_dashes.setter
    def show_dashes(self, value):
        self.timing._show_dashes = value

    @property
    def show_markers(self):
        """Display streamtrace markers.

        :type: `boolean <bool>`

        Stream markers are only available for surface and volume
        type streamlines.

        You may also specify the `size <Streamtraces.marker_size>`,
        `color <Streamtraces.marker_color>`, and
        `shape <Streamtraces.marker_symbol>` of the markers.

        Example usage::

            >>> plot.streamtraces.show_markers = True

        """
        return self.timing._show_markers

    @show_markers.setter
    def show_markers(self, value):
        self.timing._show_markers = value

    @property
    def marker_color(self):
        """`Color` of the streamline markers.

        :type: `Color` or `ContourGroup`

        Streamtrace markers can be a solid color or be colored by a
        `ContourGroup` as obtained through the ``plot.contour`` property.

        Example usage::

            >>> plot.streamtraces.marker_color = Color.Blue
        """

        return self.timing._marker_color

    @marker_color.setter
    def marker_color(self, value):
        self.timing._marker_color = value

    @property
    def marker_size(self):
        """Size of streamline markers.

         :type: `float`

         Example usage::

            >>> plot.streamtraces.marker_size = 1.1
         """
        return self.timing._marker_size

    @marker_size.setter
    def marker_size(self, value):
        self.timing._marker_size = value

    @property
    def dash_skip(self):
        """Number of time deltas used for the "off" sections of the streamlines.

        .. note::
            The ``dash_skip`` value must be greater than 0.

        :type: `integer <int>`

        Example usage::

            >>> plot.streamtraces.dash_skip = 2
        """

        return int(self.timing._dash_skip)

    @dash_skip.setter
    def dash_skip(self, value):
        self.timing._dash_skip = value

    @property
    def marker_symbol_type(self):
        """The `SymbolType` to use for stream markers.

        :type: `SymbolType`

        This sets the active symbol type for streamtrace markers.
        Use `Streamtraces.marker_symbol` to access the symbol::

            >>> from tecplot.constant import SymbolType
            >>> streamtrace = plot.streamtraces
            >>> streamtraces.marker_symbol_type = SymbolType.Text
            >>> symbol = streamtraces.marker_symbol(SymbolType.Text)
            >>> symbol.text = 'a'
        """
        return self.timing._marker_symbol_type

    @marker_symbol_type.setter
    def marker_symbol_type(self, value):
        self.timing._marker_symbol_type = value

    def marker_symbol(self, symbol_type=None):
        """Returns a streamline symbol style object.

        Parameters:
            symbol_type (`SymbolType`, optional): The type of symbol to return.
                By default, this will return the active marker symbol type
                which is obtained from `Streamtraces.marker_symbol_type`.


        Returns: `TextSymbol` or `GeometrySymbol`, depending on `marker_symbol_type`

        Example usage::

            >>> from tecplot.constant import SymbolType
            >>> streamtrace = plot.streamtraces
            >>> streamtraces.marker_symbol_type = SymbolType.Text
            >>> symbol = streamtraces.marker_symbol(SymbolType.Text)
            >>> symbol.text = 'a'
        """
        return self.timing._marker_symbol(symbol_type)

    @property
    def show_arrows(self):
        """Display arrowheads along all streamlines.

        :type: `boolean <bool>`

        Example usage::

            >>> plot.streamtraces.show_arrows = True
        """
        return self._get_style(bool, sv.ADDARROWS)

    @show_arrows.setter
    def show_arrows(self, value):
        self._set_style(bool(value), sv.ADDARROWS)

    @property
    def arrowhead_size(self):
        """Arrowhead size as a percentage of frame height.

        :type: `float`

        Recommend values are one of 1, 3, 5, 8, or 12.

        Example usage::

            >>> plot.streamtraces.show_arrows = True
            >>> plot.streamtraces.arrowhead_size = 1.0
        """
        return self._get_style(float, sv.ARROWHEADSIZE)

    @arrowhead_size.setter
    def arrowhead_size(self, value):
        self._set_style(float(value), sv.ARROWHEADSIZE)

    @property
    def arrowhead_spacing(self):
        """Distance between arrowheads in terms of Y-frame units.

        :type: `float`

        For example, a value of 10 will space arrowheads approximately
        ten percent of the frame height apart from each other along each
        streamline.

        Example usage::

            >>> plot.streamtraces.show_arrows = True
            >>> plot.streamtraces.arrowhead_spacing = 10
        """
        return self._get_style(float, sv.ARROWHEADSPACING)

    @arrowhead_spacing.setter
    def arrowhead_spacing(self, value):
        self._set_style(float(value), sv.ARROWHEADSPACING)

    @property
    def step_size(self):
        """Maximum fraction of the distance across a cell that a streamtrace
        moves in one step.

        :type: `float`

        The step size is the maximum fraction of the distance across a cell
        that a streamtrace moves in one step. A streamtrace adjusts its
        step size between `step_size` and `min_step_size`, depending
        on local curvature of the streamtrace.

        A typical value (and the default) is **0.25**, which results in
        four integration steps through each cell or element.
        The value for Step Size affects the accuracy of the integration.

        .. warning::
            Setting step size too small can result in round-off errors,
            while setting it too large can result in truncation
            errors and missed cells.

        Example usage::

            >>> plot.streamtraces.step_size = .25
        """
        return self._get_style(float, sv.CELLFRACTION)

    @step_size.setter
    def step_size(self, value):
        self._set_style(float(value), sv.CELLFRACTION)

    @property
    def color(self):
        """Color of streamtraces line (not rods or ribbons).

        :type: `Color` or `ContourGroup`

        Streamtraces can be a solid color or be colored by a
        `ContourGroup` as obtained through the ``plot.contour`` property.

        Example usage::

            >>> plot.streamtraces.color = Color.Red
        """
        return color_spec(self._get_style(Color, sv.COLOR),
                          self.plot)

    @color.setter
    def color(self, value):
        self._set_style(color_spec(value), sv.COLOR)

    @property
    def line_thickness(self):
        """Streamtrace line thickness.

        :type: `float`

        Line thickness as a percentage of the frame height for 2D lines,
        or a percentage of the median axis length for 3D surface lines and
        volume lines.

        Suggested values are .02, .1, .4, .8, 1.5

        Example usage::

            >>> plot.streamtraces.line_thickness = 1.1
        """
        return self._get_style(float, sv.LINETHICKNESS)

    @line_thickness.setter
    def line_thickness(self, value):
        self._set_style(float(value), sv.LINETHICKNESS)

    @property
    def max_steps(self):
        """Maximum number of steps before the streamtrace is terminated.

        :type: `integer <int>`

        `max_steps` prevents streamtraces from spinning forever in a vortex,
        or from wandering into a region where the vector components are very
        small, very random, or both.

        If a small `step_size` is selected, the `max_steps`
        should be a large value.

        Example usage::

            >>> plot.streamtraces.max_steps = 5000
        """
        return self._get_style(int, sv.MAXSTEPS)

    @max_steps.setter
    def max_steps(self, value):
        self._set_style(int(value), sv.MAXSTEPS)

    @property
    def min_step_size(self):
        """Smallest step size to use as a percentage of cell distance.

        A typical minimum step size value is 0.00001, which is the default.

        :type: `float`

        .. Warning::
            Setting this too small results in integration problems. Setting this
            greater than or equal to the `step_size` results in a constant step
            size.

        Example usage::

            >>> plot.streamtraces.min_step_size = .0002
        """

        return self._get_style(float, sv.MINCELLFRACTION)

    @min_step_size.setter
    def min_step_size(self, value):
        self._set_style(float(value), sv.MINCELLFRACTION)

    @property
    def obey_source_zone_blanking(self):
        """Obey source zone blanking.

        :type: `boolean <bool>`

        When `True`, streamtraces are generated for non-blanked regions only.
        When `False`, streamtraces are generated for both blanked and
        unblanked regions.

        Example usage::

            >>> plot.streamtraces.obey_source_zone_blanking = True
        """
        return self._get_style(bool, sv.OBEYSOURCEZONEBLANKING)

    @obey_source_zone_blanking.setter
    def obey_source_zone_blanking(self, value):
        self._set_style(bool(value), sv.OBEYSOURCEZONEBLANKING)

    @property
    def show_paths(self):
        """Draw streamtrace paths (lines, ribbons, or rods).

        :type: `boolean <bool>`

        A streamtrace path may be a line, ribbon or rod.

        Example usage::

            >>> plot.streamtraces.show_paths = True

        See also `Streamtraces.show_markers`
        """
        return self._get_style(bool, sv.SHOWPATHS)

    @show_paths.setter
    def show_paths(self, value):
        self._set_style(bool(value), sv.SHOWPATHS)

    def add_on_zone_surface(self, zones, stream_type, num_seed_points=10,
                            direction=StreamDir.Both):
        """Add streamtraces to one or more zones in a plot.

        The plot type must be either `Cartesian2D` or `Cartesian3D`.

        .. note:: For volume zones the streamtraces are propagated from the surfaces of the volume.

        Parameters:
            zones (`set` of `integers <int>`): Set of `Zones <data_access>`
                on which to add streamtraces.

            stream_type: (`Streamtrace`): Type of streamtraces to add.

            num_seed_points: (`integer <int>`, optional):
                Number of seed points for distributing along a rake or on
                defined surfaces.

            direction: (`StreamDir`, optional): Direction of propagation
                of the streamtraces being added.

        .. code-block:: python
            :emphasize-lines: 19-21

            import tecplot
            from tecplot.constant import *
            import os

            examples_dir = tecplot.session.tecplot_examples_directory()
            datafile = os.path.join(examples_dir, 'OneraM6wing', 'OneraM6_SU2_RANS.plt')
            dataset = tecplot.data.load_tecplot(datafile)

            frame = tecplot.active_frame()
            frame.plot_type = tecplot.constant.PlotType.Cartesian3D

            plot = frame.plot()

            plot.vector.u_variable_index = 3
            plot.vector.v_variable_index = 4
            plot.vector.w_variable_index = 5
            plot.show_streamtraces = True

            plot.streamtraces.add_on_zone_surface(
                zones=[1],  # Add streamtraces on 2nd zone only
                stream_type=Streamtrace.VolumeLine)
            tecplot.export.save_png('streamtrace_add_on_zone_surface.png', 600)

        .. figure:: /_static/images/streamtrace_add_on_zone_surface.png
            :width: 300px
            :figwidth: 300px

        """

        if __debug__:
            sdk_version_required = (2017, 2, 0, 77799)
            if sdk_version_info < sdk_version_required:
                raise TecplotOutOfDateEngineError(
                    sdk_version_required,
                    'Streamtraces.add_on_zone_surface() not supported.')

        with IndexSet(zones) as zone_set:
            self._add(stream_type=stream_type,
                      distribution_region=
                      DistributionRegion.SurfacesOfSuppliedZones,
                      stream_direction=direction,
                      num_points=num_seed_points, zones=zone_set)

    def add_rake(self, start_position, end_position, stream_type,
                 num_seed_points=10, direction=StreamDir.Both):
        """Add a rake of streamtraces to the plot of the current frame.

        The plot type must be either `Cartesian2D` or `Cartesian3D`.

        Parameters:
            start_position: (2- or 3- `tuple` of `floats <float>`):
                Pass a 2-`tuple` of `float` for a
                `Cartesian2DFieldPlot`, or a 3-`tuple` of `float` for a
                `Cartesian3DFieldPlot`.

            end_position: (2- or 3- `tuple` of `floats <float>`):
                Pass a 2-`tuple` of `float` for a
                `Cartesian2DFieldPlot`, or a 3-`tuple` of `float` for a
                `Cartesian3DFieldPlot`.

            stream_type: (`Streamtrace`): Type of streamtraces to add.

            num_seed_points: (`integer <int>`, optional):
                Number of seed points for distributing along a rake or on
                defined surfaces.

            direction: (`StreamDir`, optional): Direction of propagation
                of the streamtraces being added.

        .. code-block:: python
            :emphasize-lines: 23-25

            import tecplot
            from tecplot.constant import *
            import os

            examples_dir = tecplot.session.tecplot_examples_directory()
            datafile = os.path.join(examples_dir, '3D_Volume', 'eddy.plt')
            dataset = tecplot.data.load_tecplot(datafile)

            frame = tecplot.active_frame()
            frame.plot_type = tecplot.constant.PlotType.Cartesian3D

            plot = frame.plot()
            plot.fieldmap(0).surfaces.surfaces_to_plot = SurfacesToPlot.BoundaryFaces
            plot.show_mesh = True
            plot.show_shade = False

            plot.vector.u_variable_index = 4
            plot.vector.v_variable_index = 5
            plot.vector.w_variable_index = 6
            plot.show_streamtraces = True

            streamtraces = plot.streamtraces
            streamtraces.add_rake(start_position=[.5, .5, .5],
                                  end_position=[20, 20, 20],
                                  stream_type=Streamtrace.VolumeLine)
            tecplot.export.save_png('streamtrace_add_rake.png', 600)

        .. figure:: /_static/images/streamtrace_add_rake.png
            :width: 300px
            :figwidth: 300px

        """

        self._add(stream_type, DistributionRegion.Rake, direction,
                  num_seed_points, start_position, end_position)

    def add(self, seed_point, stream_type, direction=StreamDir.Both):
        """Add a single streamtrace to the plot of the current frame.

        The plot type must be either `Cartesian2D` or `Cartesian3D`.

        Parameters:
            seed_point: (2- or 3- `tuple` of `floats <float>`):
                Pass a 2-`tuple` of `float` for a
                `Cartesian2DFieldPlot`, or a 3-`tuple` of `float` for a
                `Cartesian3DFieldPlot`.

            stream_type: (`Streamtrace`): Type of streamtraces to add.

            direction: (`StreamDir`, optional): Direction of propagation
                of the streamtraces being added.

        .. note::
            *stream_type* is automatically set to `Streamtrace.SurfaceLine`
            if the plot type is `Cartesian2DFieldPlot`. The only stream
            type available for 2D plots is `Streamtrace.SurfaceLine`.

        Raises:
            `TecplotSystemError`: The streamtraces could not be added.

        .. code-block:: python
            :emphasize-lines: 21-21

            import tecplot
            from tecplot.constant import *
            import os

            examples_dir = tecplot.session.tecplot_examples_directory()
            datafile = os.path.join(examples_dir, '3D_Volume', 'eddy.plt')
            dataset = tecplot.data.load_tecplot(datafile)

            frame = tecplot.active_frame()
            frame.plot_type = tecplot.constant.PlotType.Cartesian3D

            plot = frame.plot()
            plot.fieldmap(0).surfaces.surfaces_to_plot = SurfacesToPlot.BoundaryFaces
            plot.show_mesh = True
            plot.show_shade = False

            plot.vector.u_variable_index = 4
            plot.vector.v_variable_index = 5
            plot.vector.w_variable_index = 6
            plot.show_streamtraces = True
            plot.streamtraces.add([3, 4, 5], Streamtrace.VolumeLine)
            tecplot.export.save_png('streamtrace_add_xyz.png', 600)

        .. figure:: /_static/images/streamtrace_add_xyz.png
            :width: 300px
            :figwidth: 300px

        """

        self._add(stream_type=stream_type,
                  distribution_region=DistributionRegion.Point,
                  stream_direction=direction,
                  start_position=seed_point,
                  end_position=seed_point if stream_type in (
                     Streamtrace.SurfaceRibbon, Streamtrace.VolumeRibbon,
                     Streamtrace.VolumeRod) else None)

    @lock()
    def _add(self, stream_type, distribution_region, stream_direction,
             num_points=None,
             start_position=None,
             end_position=None,
             zones=None):

        def _assign_position(arg_list, point_ordinal, value):
            try:
                if __debug__:
                    frame = self.plot.frame
                    if frame.plot_type == PlotType.Cartesian2D and (
                                len(value) != 2):
                        raise TecplotTypeError(
                                '[x,y] position for streamtrace '
                                'requires 2D plot type.')
                    elif frame.plot_type == PlotType.Cartesian3D and (
                                len(value) != 3):
                        raise TecplotTypeError(
                                '[x,y] position for streamtrace '
                                'requires 2D plot type.')

                arg_list['X' + point_ordinal], arg_list['Y' + point_ordinal] = (
                    float(value[0]), float(value[1]))
                if len(value) == 3:
                    arg_list['Z' + point_ordinal] = float(value[2])
            except TypeError:
                raise TecplotTypeError('streamtrace position must be a '
                                       '2- or 3- tuple of floats.')

        with ArgList() as arglist:
            arglist[sv.STREAMTYPE] = stream_type
            arglist[sv.DISTRIBUTIONREGION] = distribution_region
            arglist[sv.STREAMDIRECTION] = stream_direction
            if zones is not None:
                arglist[sv.ZONESET] = zones

            if num_points is not None:
                arglist[sv.NUMPTS] = int(num_points)
            if start_position is not None:
                _assign_position(arglist, '1', start_position)
            if end_position is not None:
                _assign_position(arglist, '2', end_position)

            with self.plot.frame.activated():
                if not _tecutil.StreamtraceAddX(arglist):
                    raise TecplotSystemError()

    @lock()
    def delete_all(self):
        """Delete all streamtraces for the current plot type.

        2D and 3D streamtraces are independent of each other.

        If the plot type is `Cartesian2D`, all 2D streamtraces are deleted.
        If the plot type is `Cartesian3D`, all 3D streamtraces are deleted.

        Raises:
            `TecplotSystemError`: The streamtraces could not be deleted.

        Example usage::

            >>> plot.streamtraces.delete_all()
        """

        with self.plot.frame.activated():
            if not _tecutil.StreamtraceDeleteAll():
                raise TecplotSystemError()

    @lock()
    def delete_range(self, range_start, range_end):
        """Delete a range of streamtraces.

        Parameters:
            range_start: (`integer <int>`): 0-based start streamtrace number to delete.

            range_end: (`integer <int>`): 0-based end streamtrace number to delete.

        Raises:
            `TecplotSystemError`: The streamtraces in the range could not be deleted.

        Example usage::

            >>> # Delete the first 100 streamtraces
            >>> plot.streamtraces.delete_range(0, 99)
        """

        with self.plot.frame.activated():
            if not _tecutil.StreamtraceDeleteRange(range_start + 1, range_end + 1):
                raise TecplotSystemError()

    @lock()
    def set_termination_line(self, line_points):
        """Set the position of the termination line for streamtraces.

        Parameters:
            line_points: (array of `float` `tuple`) Points of the termination line.

        Raises:
            `TecplotSystemError`: Termination line could not be set.

        Example usage::

            >>> # Multi-segment line between points (0,0)-(5,8)-(3,6)
            >>> line_points = [(0, 0), (5, 8), (3,6)]
            >>> plot.streamtraces.set_termination_line(line_points)
        """

        num_points = len(line_points)

        #
        # __mul__() is used below because the alternative:
        #  (c_double * num_points)(*P[0]... causes a lint warning in PyCharm
        #
        x_points = (ctypes.c_double.__mul__(num_points)
                    )(*[P[0] for P in line_points])
        y_points = (ctypes.c_double.__mul__(num_points)
                    )(*[P[1] for P in line_points])

        with self.plot.frame.activated():
            if not _tecutil.StreamtraceSetTermLine(
                    len(line_points),
                    x_points,
                    y_points):
                raise TecplotSystemError()

    @property
    def count(self):
        """Query the number of active streamtraces for the current plot type.

        Returns:
            `integer <int>`

        .. note:: This property is read-only.

        >>> num_active_streamtraces = plot.streamtraces.count

        """
        with self.plot.frame.activated():
            return _tecutil.StreamtraceGetCount()

    def position(self, stream_number):
        """Query the starting position of a streamtrace.

        Parameters:
            stream_number: (`integer <int>`): 0-based stream number to query.

        Returns:
            `tuple` of `floats <float>`

        Get the position of streamtrace number 3::

            >>> position = plot.streamtraces.position(2) # Note: 0-based
            >>> position.x  # == position[0]
            0.1
            >>> position.y  # == position[1]
            0.2
            >>> position.z  # == position[2]
            0.3
            """

        with self.plot.frame.activated():
            position = _tecutil.StreamtraceGetPos(stream_number + 1)
            return _XYZ(position[0], position[1], position[2])

    def streamtrace_type(self, stream_number):
        """Query the type of a streamtrace by streamtrace number.

        Parameters:
            stream_number: (`integer <int>`): 0-based stream number to query.

        Returns:
            `Streamtrace`

        Get the type of streamtrace 3. Note 0-based stream number::

            >>> streamtrace_type = plot.streamtraces.streamtrace_type(2) # streamtrace 3
            >>> streamtrace_type
            <Streamtrace.VolumeLine: 2>
        """

        with self.plot.frame.activated():
            return Streamtrace(
                _tecutil.StreamtraceGetType(stream_number+1))

    @property
    def has_terminating_line(self):
        """Determine if the streamtraces have the terminating line.

        .. note:: This property is read-only.

        Returns:
            `boolean <bool>`. `True` if the streamtraces have the terminating
            line, `False` otherwise.

        Example usage::

            >>> has_terminating_line = plot.streamtraces.has_terminating_line

        """
        with self.plot.frame.activated():
            with lock():
                return _tecutil.StreamtraceHasTermLine()

    @property
    def are_active(self):
        """Determine if there are active streamtraces in the current plot type.

        .. note:: This property is read-only.

        Returns:
            `boolean <bool>`. `True` if there are active streamtraces
            in the current plot type.

        Example usage::

            >>> streamtraces_are_active = plot.streamtraces.are_active
        """
        with self.plot.frame.activated():
            return _tecutil.QueryStreamtracesAreActive()
