#!/usr/bin/env python
#
# canvasopts.py - The SliceCanvasOpts and LightBoxCanvasOpts classes.
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""This module provides the following classes:

  - :class:`SliceCanvasOpts` (for the :class:`.SliceCanvas`)
  - :class:`LightBoxCanvasOpts` (for the :class:`.LightBoxCanvas`)
  - :class:`Scene3DCanvasOpts` (for the :class:`.Scene3DCanvas`)

These classes contain the definitions of properties which are available on the
corresponding canvas class.

These classes are defined independently of the :class:`.SliceCanvas` (and
other) classes so they can be inspected without having to import the
:mod:`.slicecanvas` (and other) modules, e.g. during command line argument
parsing.
"""


import copy

import numpy as np

import fsleyes_props as props


class SliceCanvasOpts(props.HasProperties):
    """The ``SliceCanvasOpts`` class defines all of the display settings
    for a :class:`.SliceCanvas`.
    """


    pos = props.Point(ndims=3)
    """The currently displayed position.

    The ``pos.x`` and ``pos.y`` positions denote the position of a *cursor*,
    which is highlighted with crosshairs (see the :attr:`showCursor`
    property). The ``pos.z`` position specifies the currently displayed slice.
    """


    zoom = props.Percentage(minval=100.0,
                            maxval=5000.0,
                            default=100.0,
                            clamped=False)
    """The :attr:`.DisplayContext.bounds` are divided by this zoom
    factor to produce the canvas display bounds.
    """


    displayBounds = props.Bounds(ndims=2, clamped=False)
    """The display bound x/y values specify the horizontal/vertical display
    range of the canvas, in display coordinates. This may be a larger area
    than the size of the displayed overlays, as it is adjusted to preserve
    the aspect ratio.
    """


    showCursor = props.Boolean(default=True)
    """If ``False``, the crosshairs which show the current cursor location
    will not be drawn.
    """


    cursorGap = props.Boolean(default=False)
    """If ``True``, and the currently selected overlay is a :class:`.Nifti`
    instance, a gap will be shown at the cursor centre (i.e. the current
    voxel).
    """


    zax = props.Choice((0, 1, 2),
                       alternates=[['x', 'X'], ['y', 'Y'], ['z', 'Z']],
                       allowStr=True)
    """The display coordinate system axis to be used as the screen *depth*
    axis. The :meth:`xax` and :meth:`yax` attributes are derived from this
    property:

     - If ``zax == 0``, ``xax, yax == 1, 2``
     - If ``zax == 1``, ``xax, yax == 0, 2``
     - If ``zax == 2``, ``xax, yax == 0, 1``
    """


    invertX = props.Boolean(default=False)
    """If ``True``, the display is inverted along the X (horizontal screen)
    axis.
    """


    invertY = props.Boolean(default=False)
    """If ``True``, the display is inverted along the Y (vertical screen)
    axis.
    """


    cursorColour = props.Colour(default=(0, 1, 0))
    """Canvas cursor colour."""


    bgColour = props.Colour(default=(0, 0, 0))
    """Canvas background colour."""


    renderMode = props.Choice(('onscreen', 'offscreen', 'prerender'))
    """How the :class:`.GLObject` instances are rendered to the canvas.

    See the :class:`.SliceCanvas` for more details.
    """


    highDpi = props.Boolean(default=False)
    """If FSLeyes is being displayed on a high-DPI screen, try to display
    the scene at full resolution.
    """


    def __init__(self):
        """Create a ``SliceCanvasOpts`` instance. """

        self.__name = '{}_{}'.format(type(self).__name__, id(self))
        self.__xax  = 0
        self.__yax  = 0

        self.addListener('zax', self.__name, self.__zaxChanged, immediate=True)
        self.__zaxChanged()


    def __zaxChanged(self, *a):
        """Calle when the :attr:`zax` property changes. Derives the
        ``xax`` and ``yax`` values.
        """

        dims = list(range(3))
        dims.pop(self.zax)
        self.__xax = dims[0]
        self.__yax = dims[1]


    @property
    def xax(self):
        """The display coordinate system axis which maps to the X (horizontal)
        canvas axis.
        """
        return self.__xax


    @property
    def yax(self):
        """The display coordinate system axis which maps to the Y (vertical)
        canvas axis.
        """
        return self.__yax


class LightBoxCanvasOpts(SliceCanvasOpts):
    """The ``LightBoxCanvasOpts`` class defines the display settings
    available on :class:`.LightBoxCanvas` instances.
    """


    sliceSpacing = props.Real(clamped=True,
                              minval=0.1,
                              maxval=30.0,
                              default=1.0)
    """This property controls the spacing between slices in the display
    coordinate system.
    """


    ncols = props.Int(clamped=True, minval=1, maxval=100, default=5)
    """This property controls the number of slices to be displayed on a
    single row.
    """


    nrows = props.Int(clamped=True, minval=1, maxval=100, default=4)
    """This property controls the number of rows to be displayed on the
    canvas.
    """


    topRow = props.Int(clamped=True, minval=0, maxval=20, default=0)
    """This property controls the (0-indexed) row to be displayed at the top
    of the canvas, thus providing the ability to scroll through the slices.
    """


    zrange = props.Bounds(ndims=1)
    """This property controls the range, in display coordinates, of the slices
    to be displayed.
    """


    showGridLines = props.Boolean(default=False)
    """If ``True``, grid lines are drawn between the displayed slices. """


    highlightSlice = props.Boolean(default=False)
    """If ``True``, a box will be drawn around the slice containing the current
    location.
    """


class Scene3DCanvasOpts(props.HasProperties):
    """The ``Scene3DCanvasOpts`` class defines the display settings
    available on :class:`.Scene3DCanvas` instances.
    """

    pos = copy.copy(SliceCanvasOpts.pos)
    """Current cursor position in the display coordinate system. The dimensions
    are in the same ordering as the display coordinate system, in contrast
    to the :attr:`SliceCanvasOpts.pos` property.
    """


    showCursor   = copy.copy(SliceCanvasOpts.showCursor)
    cursorColour = copy.copy(SliceCanvasOpts.cursorColour)
    bgColour     = copy.copy(SliceCanvasOpts.bgColour)
    zoom         = copy.copy(SliceCanvasOpts.zoom)
    highDpi      = copy.copy(SliceCanvasOpts.highDpi)


    showLegend = props.Boolean(default=True)
    """If ``True``, an orientation guide will be shown on the canvas. """


    legendColour = props.Colour(default=(0, 1, 0))
    """Colour to use for the legend text."""


    occlusion = props.Boolean(default=True)
    """If ``True``, objects closer to the camera will occlude objects
    further away. Toggles ``gl.DEPTH_TEST``.
    """


    light = props.Boolean(default=True)
    """If ``True``, a lighting effect is applied to compatible overlays
    in the scene.
    """


    lightPos = props.Point(ndims=3)
    """Light position in the display coordinate system. """


    offset = props.Point(ndims=2)
    """An offset, in X/Y pixels normalised to the range ``[-1, 1]``, from the
    centre of the ``Scene3DCanvas``.
    """


    rotation = props.Array(
        dtype=np.float64,
        shape=(3, 3),
        resizable=False,
        default=[[1, 0, 0], [0, 1, 0], [0, 0, 1]])
    """A rotation matrix which defines the current ``Scene3DCanvas`` view
    orientation. This rotation is defined in terms of the display coordinate
    system (defined by the :class:`.DisplayContext.bounds`), and applied to
    the scene that is being displayed.
    """

84ef044707d45870c3055b8151a4377e38028ddc
<mask token>


class SliceCanvasOpts(props.HasProperties):
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>


class LightBoxCanvasOpts(SliceCanvasOpts):
    """The ``LightBoxCanvasOpts`` class defines the display settings
    available on :class:`.LightBoxCanvas` instances.
    """
    sliceSpacing = props.Real(clamped=True, minval=0.1, maxval=30.0,
        default=1.0)
    """This property controls the spacing between slices in the display
    coordinate system.
    """
    ncols = props.Int(clamped=True, minval=1, maxval=100, default=5)
    """This property controls the number of slices to be displayed on a
    single row.
    """
    nrows = props.Int(clamped=True, minval=1, maxval=100, default=4)
    """This property controls the number of rows to be displayed on the
    canvas.
    """
    topRow = props.Int(clamped=True, minval=0, maxval=20, default=0)
    """This property controls the (0-indexed) row to be displayed at the top
    of the canvas, thus providing the ability to scroll through the slices.
    """
    zrange = props.Bounds(ndims=1)
    """This property controls the range, in display coordinates, of the slices
    to be displayed.
    """
    showGridLines = props.Boolean(default=False)
    """If ``True``, grid lines are drawn between the displayed slices. """
    highlightSlice = props.Boolean(default=False)
    """If ``True``, a box will be drawn around the slice containing the current
    location.
    """


class Scene3DCanvasOpts(props.HasProperties):
    """The ``Scene3DCanvasOpts`` class defines the display settings
    available on :class:`.Scene3DCanvas` instances.
    """
    pos = copy.copy(SliceCanvasOpts.pos)
    """Current cursor position in the display coordinate system. The dimensions
    are in the same ordering as the display coordinate system, in contrast
    to the :attr:`SliceCanvasOpts.pos` property.
    """
    showCursor = copy.copy(SliceCanvasOpts.showCursor)
    cursorColour = copy.copy(SliceCanvasOpts.cursorColour)
    bgColour = copy.copy(SliceCanvasOpts.bgColour)
    zoom = copy.copy(SliceCanvasOpts.zoom)
    highDpi = copy.copy(SliceCanvasOpts.highDpi)
    showLegend = props.Boolean(default=True)
    """If ``True``, an orientation guide will be shown on the canvas. """
    legendColour = props.Colour(default=(0, 1, 0))
    """Colour to use for the legend text."""
    occlusion = props.Boolean(default=True)
    """If ``True``, objects closer to the camera will occlude objects
    further away. Toggles ``gl.DEPTH_TEST``.
    """
    light = props.Boolean(default=True)
    """If ``True``, a lighting effect is applied to compatible overlays
    in the scene.
    """
    lightPos = props.Point(ndims=3)
    """Light position in the display coordinate system. """
    offset = props.Point(ndims=2)
    """An offset, in X/Y pixels normalised to the range ``[-1, 1]``, from the
    centre of the ``Scene3DCanvas``.
    """
    rotation = props.Array(dtype=np.float64, shape=(3, 3), resizable=False,
        default=[[1, 0, 0], [0, 1, 0], [0, 0, 1]])
    """A rotation matrix which defines the current ``Scene3DCanvas`` view
    orientation. This rotation is defined in terms of the display coordinate
    system (defined by the :class:`.DisplayContext.bounds`), and applied to
    the scene that is being displayed.
    """

<mask token>


class SliceCanvasOpts(props.HasProperties):
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>

    def __init__(self):
        """Create a ``SliceCanvasOpts`` instance. """
        self.__name = '{}_{}'.format(type(self).__name__, id(self))
        self.__xax = 0
        self.__yax = 0
        self.addListener('zax', self.__name, self.__zaxChanged, immediate=True)
        self.__zaxChanged()

    def __zaxChanged(self, *a):
        """Calle when the :attr:`zax` property changes. Derives the
        ``xax`` and ``yax`` values.
        """
        dims = list(range(3))
        dims.pop(self.zax)
        self.__xax = dims[0]
        self.__yax = dims[1]
    <mask token>

    @property
    def yax(self):
        """The display coordinate system axis which maps to the Y (vertical)
        canvas axis.
        """
        return self.__yax


class LightBoxCanvasOpts(SliceCanvasOpts):
    """The ``LightBoxCanvasOpts`` class defines the display settings
    available on :class:`.LightBoxCanvas` instances.
    """
    sliceSpacing = props.Real(clamped=True, minval=0.1, maxval=30.0,
        default=1.0)
    """This property controls the spacing between slices in the display
    coordinate system.
    """
    ncols = props.Int(clamped=True, minval=1, maxval=100, default=5)
    """This property controls the number of slices to be displayed on a
    single row.
    """
    nrows = props.Int(clamped=True, minval=1, maxval=100, default=4)
    """This property controls the number of rows to be displayed on the
    canvas.
    """
    topRow = props.Int(clamped=True, minval=0, maxval=20, default=0)
    """This property controls the (0-indexed) row to be displayed at the top
    of the canvas, thus providing the ability to scroll through the slices.
    """
    zrange = props.Bounds(ndims=1)
    """This property controls the range, in display coordinates, of the slices
    to be displayed.
    """
    showGridLines = props.Boolean(default=False)
    """If ``True``, grid lines are drawn between the displayed slices. """
    highlightSlice = props.Boolean(default=False)
    """If ``True``, a box will be drawn around the slice containing the current
    location.
    """


class Scene3DCanvasOpts(props.HasProperties):
    """The ``Scene3DCanvasOpts`` class defines the display settings
    available on :class:`.Scene3DCanvas` instances.
    """
    pos = copy.copy(SliceCanvasOpts.pos)
    """Current cursor position in the display coordinate system. The dimensions
    are in the same ordering as the display coordinate system, in contrast
    to the :attr:`SliceCanvasOpts.pos` property.
    """
    showCursor = copy.copy(SliceCanvasOpts.showCursor)
    cursorColour = copy.copy(SliceCanvasOpts.cursorColour)
    bgColour = copy.copy(SliceCanvasOpts.bgColour)
    zoom = copy.copy(SliceCanvasOpts.zoom)
    highDpi = copy.copy(SliceCanvasOpts.highDpi)
    showLegend = props.Boolean(default=True)
    """If ``True``, an orientation guide will be shown on the canvas. """
    legendColour = props.Colour(default=(0, 1, 0))
    """Colour to use for the legend text."""
    occlusion = props.Boolean(default=True)
    """If ``True``, objects closer to the camera will occlude objects
    further away. Toggles ``gl.DEPTH_TEST``.
    """
    light = props.Boolean(default=True)
    """If ``True``, a lighting effect is applied to compatible overlays
    in the scene.
    """
    lightPos = props.Point(ndims=3)
    """Light position in the display coordinate system. """
    offset = props.Point(ndims=2)
    """An offset, in X/Y pixels normalised to the range ``[-1, 1]``, from the
    centre of the ``Scene3DCanvas``.
    """
    rotation = props.Array(dtype=np.float64, shape=(3, 3), resizable=False,
        default=[[1, 0, 0], [0, 1, 0], [0, 0, 1]])
    """A rotation matrix which defines the current ``Scene3DCanvas`` view
    orientation. This rotation is defined in terms of the display coordinate
    system (defined by the :class:`.DisplayContext.bounds`), and applied to
    the scene that is being displayed.
    """

<mask token>


class SliceCanvasOpts(props.HasProperties):
    <mask token>
    pos = props.Point(ndims=3)
    <mask token>
    zoom = props.Percentage(minval=100.0, maxval=5000.0, default=100.0,
        clamped=False)
    <mask token>
    displayBounds = props.Bounds(ndims=2, clamped=False)
    <mask token>
    showCursor = props.Boolean(default=True)
    <mask token>
    cursorGap = props.Boolean(default=False)
    <mask token>
    zax = props.Choice((0, 1, 2), alternates=[['x', 'X'], ['y', 'Y'], ['z',
        'Z']], allowStr=True)
    <mask token>
    invertX = props.Boolean(default=False)
    <mask token>
    invertY = props.Boolean(default=False)
    <mask token>
    cursorColour = props.Colour(default=(0, 1, 0))
    <mask token>
    bgColour = props.Colour(default=(0, 0, 0))
    <mask token>
    renderMode = props.Choice(('onscreen', 'offscreen', 'prerender'))
    <mask token>
    highDpi = props.Boolean(default=False)
    <mask token>

    def __init__(self):
        """Create a ``SliceCanvasOpts`` instance. """
        self.__name = '{}_{}'.format(type(self).__name__, id(self))
        self.__xax = 0
        self.__yax = 0
        self.addListener('zax', self.__name, self.__zaxChanged, immediate=True)
        self.__zaxChanged()

    def __zaxChanged(self, *a):
        """Calle when the :attr:`zax` property changes. Derives the
        ``xax`` and ``yax`` values.
        """
        dims = list(range(3))
        dims.pop(self.zax)
        self.__xax = dims[0]
        self.__yax = dims[1]

    @property
    def xax(self):
        """The display coordinate system axis which maps to the X (horizontal)
        canvas axis.
        """
        return self.__xax

    @property
    def yax(self):
        """The display coordinate system axis which maps to the Y (vertical)
        canvas axis.
        """
        return self.__yax


class LightBoxCanvasOpts(SliceCanvasOpts):
    """The ``LightBoxCanvasOpts`` class defines the display settings
    available on :class:`.LightBoxCanvas` instances.
    """
    sliceSpacing = props.Real(clamped=True, minval=0.1, maxval=30.0,
        default=1.0)
    """This property controls the spacing between slices in the display
    coordinate system.
    """
    ncols = props.Int(clamped=True, minval=1, maxval=100, default=5)
    """This property controls the number of slices to be displayed on a
    single row.
    """
    nrows = props.Int(clamped=True, minval=1, maxval=100, default=4)
    """This property controls the number of rows to be displayed on the
    canvas.
    """
    topRow = props.Int(clamped=True, minval=0, maxval=20, default=0)
    """This property controls the (0-indexed) row to be displayed at the top
    of the canvas, thus providing the ability to scroll through the slices.
    """
    zrange = props.Bounds(ndims=1)
    """This property controls the range, in display coordinates, of the slices
    to be displayed.
    """
    showGridLines = props.Boolean(default=False)
    """If ``True``, grid lines are drawn between the displayed slices. """
    highlightSlice = props.Boolean(default=False)
    """If ``True``, a box will be drawn around the slice containing the current
    location.
    """


class Scene3DCanvasOpts(props.HasProperties):
    """The ``Scene3DCanvasOpts`` class defines the display settings
    available on :class:`.Scene3DCanvas` instances.
    """
    pos = copy.copy(SliceCanvasOpts.pos)
    """Current cursor position in the display coordinate system. The dimensions
    are in the same ordering as the display coordinate system, in contrast
    to the :attr:`SliceCanvasOpts.pos` property.
    """
    showCursor = copy.copy(SliceCanvasOpts.showCursor)
    cursorColour = copy.copy(SliceCanvasOpts.cursorColour)
    bgColour = copy.copy(SliceCanvasOpts.bgColour)
    zoom = copy.copy(SliceCanvasOpts.zoom)
    highDpi = copy.copy(SliceCanvasOpts.highDpi)
    showLegend = props.Boolean(default=True)
    """If ``True``, an orientation guide will be shown on the canvas. """
    legendColour = props.Colour(default=(0, 1, 0))
    """Colour to use for the legend text."""
    occlusion = props.Boolean(default=True)
    """If ``True``, objects closer to the camera will occlude objects
    further away. Toggles ``gl.DEPTH_TEST``.
    """
    light = props.Boolean(default=True)
    """If ``True``, a lighting effect is applied to compatible overlays
    in the scene.
    """
    lightPos = props.Point(ndims=3)
    """Light position in the display coordinate system. """
    offset = props.Point(ndims=2)
    """An offset, in X/Y pixels normalised to the range ``[-1, 1]``, from the
    centre of the ``Scene3DCanvas``.
    """
    rotation = props.Array(dtype=np.float64, shape=(3, 3), resizable=False,
        default=[[1, 0, 0], [0, 1, 0], [0, 0, 1]])
    """A rotation matrix which defines the current ``Scene3DCanvas`` view
    orientation. This rotation is defined in terms of the display coordinate
    system (defined by the :class:`.DisplayContext.bounds`), and applied to
    the scene that is being displayed.
    """

<mask token>


class SliceCanvasOpts(props.HasProperties):
    """The ``SliceCanvasOpts`` class defines all of the display settings
    for a :class:`.SliceCanvas`.
    """
    pos = props.Point(ndims=3)
    """The currently displayed position.

    The ``pos.x`` and ``pos.y`` positions denote the position of a *cursor*,
    which is highlighted with crosshairs (see the :attr:`showCursor`
    property). The ``pos.z`` position specifies the currently displayed slice.
    """
    zoom = props.Percentage(minval=100.0, maxval=5000.0, default=100.0,
        clamped=False)
    """The :attr:`.DisplayContext.bounds` are divided by this zoom
    factor to produce the canvas display bounds.
    """
    displayBounds = props.Bounds(ndims=2, clamped=False)
    """The display bound x/y values specify the horizontal/vertical display
    range of the canvas, in display coordinates. This may be a larger area
    than the size of the displayed overlays, as it is adjusted to preserve
    the aspect ratio.
    """
    showCursor = props.Boolean(default=True)
    """If ``False``, the crosshairs which show the current cursor location
    will not be drawn.
    """
    cursorGap = props.Boolean(default=False)
    """If ``True``, and the currently selected overlay is a :class:`.Nifti`
    instance, a gap will be shown at the cursor centre (i.e. the current
    voxel).
    """
    zax = props.Choice((0, 1, 2), alternates=[['x', 'X'], ['y', 'Y'], ['z',
        'Z']], allowStr=True)
    """The display coordinate system axis to be used as the screen *depth*
    axis. The :meth:`xax` and :meth:`yax` attributes are derived from this
    property:

     - If ``zax == 0``, ``xax, yax == 1, 2``
     - If ``zax == 1``, ``xax, yax == 0, 2``
     - If ``zax == 2``, ``xax, yax == 0, 1``
    """
    invertX = props.Boolean(default=False)
    """If ``True``, the display is inverted along the X (horizontal screen)
    axis.
    """
    invertY = props.Boolean(default=False)
    """If ``True``, the display is inverted along the Y (vertical screen)
    axis.
    """
    cursorColour = props.Colour(default=(0, 1, 0))
    """Canvas cursor colour."""
    bgColour = props.Colour(default=(0, 0, 0))
    """Canvas background colour."""
    renderMode = props.Choice(('onscreen', 'offscreen', 'prerender'))
    """How the :class:`.GLObject` instances are rendered to the canvas.

    See the :class:`.SliceCanvas` for more details.
    """
    highDpi = props.Boolean(default=False)
    """If FSLeyes is being displayed on a high-DPI screen, try to display
    the scene at full resolution.
    """

    def __init__(self):
        """Create a ``SliceCanvasOpts`` instance. """
        self.__name = '{}_{}'.format(type(self).__name__, id(self))
        self.__xax = 0
        self.__yax = 0
        self.addListener('zax', self.__name, self.__zaxChanged, immediate=True)
        self.__zaxChanged()

    def __zaxChanged(self, *a):
        """Calle when the :attr:`zax` property changes. Derives the
        ``xax`` and ``yax`` values.
        """
        dims = list(range(3))
        dims.pop(self.zax)
        self.__xax = dims[0]
        self.__yax = dims[1]

    @property
    def xax(self):
        """The display coordinate system axis which maps to the X (horizontal)
        canvas axis.
        """
        return self.__xax

    @property
    def yax(self):
        """The display coordinate system axis which maps to the Y (vertical)
        canvas axis.
        """
        return self.__yax


class LightBoxCanvasOpts(SliceCanvasOpts):
    """The ``LightBoxCanvasOpts`` class defines the display settings
    available on :class:`.LightBoxCanvas` instances.
    """
    sliceSpacing = props.Real(clamped=True, minval=0.1, maxval=30.0,
        default=1.0)
    """This property controls the spacing between slices in the display
    coordinate system.
    """
    ncols = props.Int(clamped=True, minval=1, maxval=100, default=5)
    """This property controls the number of slices to be displayed on a
    single row.
    """
    nrows = props.Int(clamped=True, minval=1, maxval=100, default=4)
    """This property controls the number of rows to be displayed on the
    canvas.
    """
    topRow = props.Int(clamped=True, minval=0, maxval=20, default=0)
    """This property controls the (0-indexed) row to be displayed at the top
    of the canvas, thus providing the ability to scroll through the slices.
    """
    zrange = props.Bounds(ndims=1)
    """This property controls the range, in display coordinates, of the slices
    to be displayed.
    """
    showGridLines = props.Boolean(default=False)
    """If ``True``, grid lines are drawn between the displayed slices. """
    highlightSlice = props.Boolean(default=False)
    """If ``True``, a box will be drawn around the slice containing the current
    location.
    """


class Scene3DCanvasOpts(props.HasProperties):
    """The ``Scene3DCanvasOpts`` class defines the display settings
    available on :class:`.Scene3DCanvas` instances.
    """
    pos = copy.copy(SliceCanvasOpts.pos)
    """Current cursor position in the display coordinate system. The dimensions
    are in the same ordering as the display coordinate system, in contrast
    to the :attr:`SliceCanvasOpts.pos` property.
    """
    showCursor = copy.copy(SliceCanvasOpts.showCursor)
    cursorColour = copy.copy(SliceCanvasOpts.cursorColour)
    bgColour = copy.copy(SliceCanvasOpts.bgColour)
    zoom = copy.copy(SliceCanvasOpts.zoom)
    highDpi = copy.copy(SliceCanvasOpts.highDpi)
    showLegend = props.Boolean(default=True)
    """If ``True``, an orientation guide will be shown on the canvas. """
    legendColour = props.Colour(default=(0, 1, 0))
    """Colour to use for the legend text."""
    occlusion = props.Boolean(default=True)
    """If ``True``, objects closer to the camera will occlude objects
    further away. Toggles ``gl.DEPTH_TEST``.
    """
    light = props.Boolean(default=True)
    """If ``True``, a lighting effect is applied to compatible overlays
    in the scene.
    """
    lightPos = props.Point(ndims=3)
    """Light position in the display coordinate system. """
    offset = props.Point(ndims=2)
    """An offset, in X/Y pixels normalised to the range ``[-1, 1]``, from the
    centre of the ``Scene3DCanvas``.
    """
    rotation = props.Array(dtype=np.float64, shape=(3, 3), resizable=False,
        default=[[1, 0, 0], [0, 1, 0], [0, 0, 1]])
    """A rotation matrix which defines the current ``Scene3DCanvas`` view
    orientation. This rotation is defined in terms of the display coordinate
    system (defined by the :class:`.DisplayContext.bounds`), and applied to
    the scene that is being displayed.
    """

#!/usr/bin/env python
#
# canvasopts.py - The SliceCanvasOpts and LightBoxCanvasOpts classes.
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""This module provides the following classes:

  - :class:`SliceCanvasOpts` (for the :class:`.SliceCanvas`)
  - :class:`LightBoxCanvasOpts` (for the :class:`.LightBoxCanvas`)
  - :class:`Scene3DCanvasOpts` (for the :class:`.Scene3DCanvas`)

These classes contain the definitions of properties which are available on the
corresponding canvas class.

These classes are defined independently of the :class:`.SliceCanvas` (and
other) classes so they can be inspected without having to import the
:mod:`.slicecanvas` (and other) modules, e.g. during command line argument
parsing.
"""


import copy

import numpy as np

import fsleyes_props as props


class SliceCanvasOpts(props.HasProperties):
    """The ``SliceCanvasOpts`` class defines all of the display settings
    for a :class:`.SliceCanvas`.
    """


    pos = props.Point(ndims=3)
    """The currently displayed position.

    The ``pos.x`` and ``pos.y`` positions denote the position of a *cursor*,
    which is highlighted with crosshairs (see the :attr:`showCursor`
    property). The ``pos.z`` position specifies the currently displayed slice.
    """


    zoom = props.Percentage(minval=100.0,
                            maxval=5000.0,
                            default=100.0,
                            clamped=False)
    """The :attr:`.DisplayContext.bounds` are divided by this zoom
    factor to produce the canvas display bounds.
    """


    displayBounds = props.Bounds(ndims=2, clamped=False)
    """The display bound x/y values specify the horizontal/vertical display
    range of the canvas, in display coordinates. This may be a larger area
    than the size of the displayed overlays, as it is adjusted to preserve
    the aspect ratio.
    """


    showCursor = props.Boolean(default=True)
    """If ``False``, the crosshairs which show the current cursor location
    will not be drawn.
    """


    cursorGap = props.Boolean(default=False)
    """If ``True``, and the currently selected overlay is a :class:`.Nifti`
    instance, a gap will be shown at the cursor centre (i.e. the current
    voxel).
    """


    zax = props.Choice((0, 1, 2),
                       alternates=[['x', 'X'], ['y', 'Y'], ['z', 'Z']],
                       allowStr=True)
    """The display coordinate system axis to be used as the screen *depth*
    axis. The :meth:`xax` and :meth:`yax` attributes are derived from this
    property:

     - If ``zax == 0``, ``xax, yax == 1, 2``
     - If ``zax == 1``, ``xax, yax == 0, 2``
     - If ``zax == 2``, ``xax, yax == 0, 1``
    """


    invertX = props.Boolean(default=False)
    """If ``True``, the display is inverted along the X (horizontal screen)
    axis.
    """


    invertY = props.Boolean(default=False)
    """If ``True``, the display is inverted along the Y (vertical screen)
    axis.
    """


    cursorColour = props.Colour(default=(0, 1, 0))
    """Canvas cursor colour."""


    bgColour = props.Colour(default=(0, 0, 0))
    """Canvas background colour."""


    renderMode = props.Choice(('onscreen', 'offscreen', 'prerender'))
    """How the :class:`.GLObject` instances are rendered to the canvas.

    See the :class:`.SliceCanvas` for more details.
    """


    highDpi = props.Boolean(default=False)
    """If FSLeyes is being displayed on a high-DPI screen, try to display
    the scene at full resolution.
    """


    def __init__(self):
        """Create a ``SliceCanvasOpts`` instance. """

        self.__name = '{}_{}'.format(type(self).__name__, id(self))
        self.__xax  = 0
        self.__yax  = 0

        self.addListener('zax', self.__name, self.__zaxChanged, immediate=True)
        self.__zaxChanged()


    def __zaxChanged(self, *a):
        """Calle when the :attr:`zax` property changes. Derives the
        ``xax`` and ``yax`` values.
        """

        dims = list(range(3))
        dims.pop(self.zax)
        self.__xax = dims[0]
        self.__yax = dims[1]


    @property
    def xax(self):
        """The display coordinate system axis which maps to the X (horizontal)
        canvas axis.
        """
        return self.__xax


    @property
    def yax(self):
        """The display coordinate system axis which maps to the Y (vertical)
        canvas axis.
        """
        return self.__yax


class LightBoxCanvasOpts(SliceCanvasOpts):
    """The ``LightBoxCanvasOpts`` class defines the display settings
    available on :class:`.LightBoxCanvas` instances.
    """


    sliceSpacing = props.Real(clamped=True,
                              minval=0.1,
                              maxval=30.0,
                              default=1.0)
    """This property controls the spacing between slices in the display
    coordinate system.
    """


    ncols = props.Int(clamped=True, minval=1, maxval=100, default=5)
    """This property controls the number of slices to be displayed on a
    single row.
    """


    nrows = props.Int(clamped=True, minval=1, maxval=100, default=4)
    """This property controls the number of rows to be displayed on the
    canvas.
    """


    topRow = props.Int(clamped=True, minval=0, maxval=20, default=0)
    """This property controls the (0-indexed) row to be displayed at the top
    of the canvas, thus providing the ability to scroll through the slices.
    """


    zrange = props.Bounds(ndims=1)
    """This property controls the range, in display coordinates, of the slices
    to be displayed.
    """


    showGridLines = props.Boolean(default=False)
    """If ``True``, grid lines are drawn between the displayed slices. """


    highlightSlice = props.Boolean(default=False)
    """If ``True``, a box will be drawn around the slice containing the current
    location.
    """


class Scene3DCanvasOpts(props.HasProperties):
    """The ``Scene3DCanvasOpts`` class defines the display settings
    available on :class:`.Scene3DCanvas` instances.
    """

    pos = copy.copy(SliceCanvasOpts.pos)
    """Current cursor position in the display coordinate system. The dimensions
    are in the same ordering as the display coordinate system, in contrast
    to the :attr:`SliceCanvasOpts.pos` property.
    """


    showCursor   = copy.copy(SliceCanvasOpts.showCursor)
    cursorColour = copy.copy(SliceCanvasOpts.cursorColour)
    bgColour     = copy.copy(SliceCanvasOpts.bgColour)
    zoom         = copy.copy(SliceCanvasOpts.zoom)
    highDpi      = copy.copy(SliceCanvasOpts.highDpi)


    showLegend = props.Boolean(default=True)
    """If ``True``, an orientation guide will be shown on the canvas. """


    legendColour = props.Colour(default=(0, 1, 0))
    """Colour to use for the legend text."""


    occlusion = props.Boolean(default=True)
    """If ``True``, objects closer to the camera will occlude objects
    further away. Toggles ``gl.DEPTH_TEST``.
    """


    light = props.Boolean(default=True)
    """If ``True``, a lighting effect is applied to compatible overlays
    in the scene.
    """


    lightPos = props.Point(ndims=3)
    """Light position in the display coordinate system. """


    offset = props.Point(ndims=2)
    """An offset, in X/Y pixels normalised to the range ``[-1, 1]``, from the
    centre of the ``Scene3DCanvas``.
    """


    rotation = props.Array(
        dtype=np.float64,
        shape=(3, 3),
        resizable=False,
        default=[[1, 0, 0], [0, 1, 0], [0, 0, 1]])
    """A rotation matrix which defines the current ``Scene3DCanvas`` view
    orientation. This rotation is defined in terms of the display coordinate
    system (defined by the :class:`.DisplayContext.bounds`), and applied to
    the scene that is being displayed.
    """
