    --------------------------------------------------------------------------------
    -- |
    -- Module      :  Graphics.Rendering.OpenGL.GL.Points
    -- Copyright   :  (c) Sven Panne 2002-2019
    -- License     :  BSD3
    --
    -- Maintainer  :  Sven Panne <svenpanne@gmail.com>
    -- Stability   :  stable
    -- Portability :  portable
    --
    -- This module corresponds to section 3.3 (Points) of the OpenGL 2.1 specs.
    --
    --------------------------------------------------------------------------------

    module Graphics.Rendering.OpenGL.GL.Points (
       -- * Point Rasterization
       pointSize, vertexProgramPointSize,

       -- * Controlling the Derived Size
       pointSizeRange, pointDistanceAttenuation,

       -- * Fading Points
       pointFadeThresholdSize,

       -- * Point Antialiasing
       pointSmooth,

       -- * Point Sprites
       pointSprite,

       -- * Implementation-Dependent Limits
       aliasedPointSizeRange, smoothPointSizeRange, smoothPointSizeGranularity
    ) where

    import Control.Monad
    import Data.StateVar
    import Foreign.Marshal.Array
    import Graphics.Rendering.OpenGL.GL.Capability
    import Graphics.Rendering.OpenGL.GL.PointParameter
    import Graphics.Rendering.OpenGL.GL.QueryUtils
    import Graphics.GL

    --------------------------------------------------------------------------------

    -- | 'pointSize' contains the rasterized diameter of both aliased and
    -- antialiased points. The initial value is 1. Using a point size other than 1
    -- has different effects, depending on whether point antialiasing is enabled
    -- (see 'pointSmooth') or point sprites are enabled (see 'pointSprite'). Both
    -- are initially disabled.
    --
    -- The specified point size is multiplied with a distance attenuation factor
    -- and clamped to the specified 'pointSizeRange', and further clamped to the
    -- implementation-dependent point size range to produce the derived point size
    -- using
    --
    -- @   /derivedSize/ = /clamp/ (/size/ * /sqrt/ (1 \/ (/a/ + /b/ * /d/ + /c/ * /d/^2)))@
    --
    -- where /d/ is the eye-coordinate distance from the eye to the vertex, and /a/,
    -- /b/, and /c/ are the distance attenuation coefficients (see
    -- 'pointDistanceAttenuation').
    --
    -- If multisampling is disabled, the computed point size is used as the point\'s
    -- width.
    --
    -- If multisampling is enabled, the point may be faded by modifying the point
    -- alpha value (see 'Graphics.Rendering.OpenGL.GL.PerFragment.sampleCoverage')
    -- instead of allowing the point width to go below a given
    -- 'pointFadeThresholdSize'. In this case, the width is further modified in
    -- the following manner:
    --
    -- @   /width/ = if /derivedSize/ >= /threshold/ then /derivedSize/ else /threshold/@
    --
    -- The point alpha value is modified by computing:
    --
    -- @   /alpha/ = if /derivedSize/ >= /threshold/ then 1 else (/derivedSize/ \/ /threshold/)^2@
    --
    -- If point antialiasing is disabled, the actual size is determined by rounding
    -- the supplied size to the nearest integer. (If the rounding results in the
    -- value 0, it is as if the point size were 1.) If the rounded size is odd,
    -- then the center point (/x/, /y/) of the pixel fragment that represents
    -- the point is computed as
    --
    -- @   (/x/, /y/) = (/floor/ /xw/ + 0.5, /floor/ /yw/ + 0.5)@
    --
    -- where /xw/ and /yw/ indicate window coordinates. All pixels that lie within
    -- the square grid of the rounded size centered at (/x/, /y/) make up the
    -- fragment. If the size is even, the center point is
    --
    -- @   (/x/, /y/) = (/floor/ (/xw/ + 0.5), /floor/ (/yw/ + 0.5))@
    --
    -- and the rasterized fragment\'s centers are the half-integer window
    -- coordinates within the square of the rounded size centered at (/x/, /y/). All
    -- pixel fragments produced in rasterizing a nonantialiased point are assigned
    -- the same associated data, that of the vertex corresponding to the point.
    --
    -- If antialiasing is enabled, then point rasterization produces a fragment for
    -- each pixel square that intersects the region lying within the circle having
    -- diameter equal to the current point size and centered at the point\'s
    -- (/xw/, /yw/). The coverage value for each fragment is the window coordinate
    -- area of the intersection of the circular region with the corresponding pixel
    -- square. This value is saved and used in the final rasterization step. The
    -- data associated with each fragment is the data associated with the point
    -- being rasterized.
    --
    -- Not all sizes are supported when point antialiasing is enabled. If an
    -- unsupported size is requested, the nearest supported size is used.  Only size
    -- 1 is guaranteed to be supported; others depend on the implementation. To
    -- query the range of supported sizes for antialiased points and the size
    -- difference between supported sizes within the range, query
    -- 'smoothPointSizeRange' and 'smoothPointSizeGranularity', respectively. For
    -- aliased points, query the supported range with 'aliasedPointSizeRange'.
    --
    -- The point size specified when 'pointSize' is set is always returned when it
    -- is queried. Clamping and rounding for aliased and antialiased points have no
    -- effect on the specified value.
    --
    -- A non-antialiased point size may be clamped to an implementation-dependent
    -- maximum. Although this maximum cannot be queried, it must be no less than the
    -- maximum value for antialiased points, rounded to the nearest integer value.
    --
    -- An 'Graphics.Rendering.OpenGL.GLU.Errors.InvalidValue' is generated if
    -- 'pointSize' is set to a value less than or equal to zero.
    --
    -- An 'Graphics.Rendering.OpenGL.GLU.Errors.InvalidOperation' is generated if
    -- 'pointSize' is set during
    -- 'Graphics.Rendering.OpenGL.GL.BeginEnd.renderPrimitive'.

    pointSize :: StateVar GLfloat
    pointSize :: StateVar GLfloat
    pointSize = IO GLfloat -> (GLfloat -> IO ()) -> StateVar GLfloat
    forall a. IO a -> (a -> IO ()) -> StateVar a
    makeStateVar ((GLfloat -> GLfloat) -> PName1F -> IO GLfloat
    forall p a. GetPName1F p => (GLfloat -> a) -> p -> IO a
    forall a. (GLfloat -> a) -> PName1F -> IO a
    getFloat1 GLfloat -> GLfloat
    forall a. a -> a
    id PName1F
    GetPointSize) GLfloat -> IO ()
    forall (m :: * -> *). MonadIO m => GLfloat -> m ()
    glPointSize

    --------------------------------------------------------------------------------

    vertexProgramPointSize :: StateVar Capability
    vertexProgramPointSize :: StateVar Capability
    vertexProgramPointSize = EnableCap -> StateVar Capability
    makeCapability EnableCap
    CapVertexProgramPointSize

    --------------------------------------------------------------------------------

    -- | The range to which the derived point size is clamped, see 'pointSize'. Note
    -- that the size is further clamped to the implementation-dependent limits, see
    -- 'aliasedPointSizeRange' and 'smoothPointSizeRange'. The initial range is
    -- (0, 1).
    --
    -- An 'Graphics.Rendering.OpenGL.GLU.Errors.InvalidValue' is generated if the
    -- lower or upper bound of the range is set to a value less than zero. If the
    -- lower bound is greater than the upper bound, the point size after clamping is
    -- undefined, but no error is generated.

    pointSizeRange :: StateVar (GLfloat, GLfloat)
    pointSizeRange :: StateVar (GLfloat, GLfloat)
    pointSizeRange =
       IO (GLfloat, GLfloat)
    -> ((GLfloat, GLfloat) -> IO ()) -> StateVar (GLfloat, GLfloat)
    forall a. IO a -> (a -> IO ()) -> StateVar a
    makeStateVar
       ((GLfloat -> GLfloat -> (GLfloat, GLfloat))
    -> IO GLfloat -> IO GLfloat -> IO (GLfloat, GLfloat)
    forall (m :: * -> *) a1 a2 r.
    Monad m =>
    (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
    liftM2 (,) ((GLfloat -> GLfloat) -> PName1F -> IO GLfloat
    forall p a. GetPName1F p => (GLfloat -> a) -> p -> IO a
    forall a. (GLfloat -> a) -> PName1F -> IO a
    getFloat1 GLfloat -> GLfloat
    forall a. a -> a
    id PName1F
    GetPointSizeMin) ((GLfloat -> GLfloat) -> PName1F -> IO GLfloat
    forall p a. GetPName1F p => (GLfloat -> a) -> p -> IO a
    forall a. (GLfloat -> a) -> PName1F -> IO a
    getFloat1 GLfloat -> GLfloat
    forall a. a -> a
    id PName1F
    GetPointSizeMax))
       (\(GLfloat
    sizeMin, GLfloat
    sizeMax) -> do PointParameter -> GLfloat -> IO ()
    pointParameterf PointParameter
    PointSizeMin GLfloat
    sizeMin
                                  PointParameter -> GLfloat -> IO ()
    pointParameterf PointParameter
    PointSizeMax GLfloat
    sizeMax)

    --------------------------------------------------------------------------------

    -- | The  constant, linear, and quadratic distance attenuation coefficients, see
    -- 'pointSize'. The initial coefficients are (1, 0, 0).

    pointDistanceAttenuation :: StateVar (GLfloat, GLfloat, GLfloat)
    pointDistanceAttenuation :: StateVar (GLfloat, GLfloat, GLfloat)
    pointDistanceAttenuation =
       IO (GLfloat, GLfloat, GLfloat)
    -> ((GLfloat, GLfloat, GLfloat) -> IO ())
    -> StateVar (GLfloat, GLfloat, GLfloat)
    forall a. IO a -> (a -> IO ()) -> StateVar a
    makeStateVar
          ((GLfloat -> GLfloat -> GLfloat -> (GLfloat, GLfloat, GLfloat))
    -> PName3F -> IO (GLfloat, GLfloat, GLfloat)
    forall p a.
    GetPName3F p =>
    (GLfloat -> GLfloat -> GLfloat -> a) -> p -> IO a
    forall a. (GLfloat -> GLfloat -> GLfloat -> a) -> PName3F -> IO a
    getFloat3 (,,) PName3F
    GetPointDistanceAttenuation)
          (\(GLfloat
    a, GLfloat
    b, GLfloat
    c) -> [GLfloat] -> (Ptr GLfloat -> IO ()) -> IO ()
    forall a b. Storable a => [a] -> (Ptr a -> IO b) -> IO b
    withArray [GLfloat
    a, GLfloat
    b, GLfloat
    c] ((Ptr GLfloat -> IO ()) -> IO ())
    -> (Ptr GLfloat -> IO ()) -> IO ()
    forall a b. (a -> b) -> a -> b
    $
                            PointParameter -> Ptr GLfloat -> IO ()
    pointParameterfv PointParameter
    PointDistanceAttenuation)

    --------------------------------------------------------------------------------

    -- | The threshold for alpha attenuation of points when multisampling is used,
    -- see 'pointSize'. The initial threshold is 1.
    --
    -- An 'Graphics.Rendering.OpenGL.GLU.Errors.InvalidValue' is generated if the
    -- threshold is set to a value less than zero.

    pointFadeThresholdSize :: StateVar GLfloat
    pointFadeThresholdSize :: StateVar GLfloat
    pointFadeThresholdSize =
       IO GLfloat -> (GLfloat -> IO ()) -> StateVar GLfloat
    forall a. IO a -> (a -> IO ()) -> StateVar a
    makeStateVar
          ((GLfloat -> GLfloat) -> PName1F -> IO GLfloat
    forall p a. GetPName1F p => (GLfloat -> a) -> p -> IO a
    forall a. (GLfloat -> a) -> PName1F -> IO a
    getFloat1 GLfloat -> GLfloat
    forall a. a -> a
    id PName1F
    GetPointFadeThresholdSize)
          (PointParameter -> GLfloat -> IO ()
    pointParameterf PointParameter
    PointFadeThresholdSize)

    --------------------------------------------------------------------------------

    -- | Controls whether point antialiasing is enabled. The initial state is
    -- 'Graphics.Rendering.OpenGL.GL.Capability.Disabled'.

    pointSmooth :: StateVar Capability
    pointSmooth :: StateVar Capability
    pointSmooth = EnableCap -> StateVar Capability
    makeCapability EnableCap
    CapPointSmooth

    --------------------------------------------------------------------------------

    -- | Controls whether point sprites are enabled. The initial state is
    -- 'Graphics.Rendering.OpenGL.GL.Capability.Disabled'. When point sprites are
    -- enabled, the state of point antialiasing (i.e. 'pointSmooth') is ignored.

    pointSprite :: StateVar Capability
    pointSprite :: StateVar Capability
    pointSprite = EnableCap -> StateVar Capability
    makeCapability EnableCap
    CapPointSprite

    --------------------------------------------------------------------------------

    -- | The smallest and largest supported size of aliased points.

    aliasedPointSizeRange :: GettableStateVar (GLfloat, GLfloat)
    aliasedPointSizeRange :: IO (GLfloat, GLfloat)
    aliasedPointSizeRange =
       IO (GLfloat, GLfloat) -> IO (GLfloat, GLfloat)
    forall a. IO a -> IO a
    makeGettableStateVar (IO (GLfloat, GLfloat) -> IO (GLfloat, GLfloat))
    -> IO (GLfloat, GLfloat) -> IO (GLfloat, GLfloat)
    forall a b. (a -> b) -> a -> b
    $ (GLfloat -> GLfloat -> (GLfloat, GLfloat))
    -> PName2F -> IO (GLfloat, GLfloat)
    forall p a. GetPName2F p => (GLfloat -> GLfloat -> a) -> p -> IO a
    forall a. (GLfloat -> GLfloat -> a) -> PName2F -> IO a
    getFloat2 (,) PName2F
    GetAliasedPointSizeRange

    -- | The smallest and largest supported size of antialiased points.

    smoothPointSizeRange :: GettableStateVar (GLfloat, GLfloat)
    smoothPointSizeRange :: IO (GLfloat, GLfloat)
    smoothPointSizeRange =
       IO (GLfloat, GLfloat) -> IO (GLfloat, GLfloat)
    forall a. IO a -> IO a
    makeGettableStateVar (IO (GLfloat, GLfloat) -> IO (GLfloat, GLfloat))
    -> IO (GLfloat, GLfloat) -> IO (GLfloat, GLfloat)
    forall a b. (a -> b) -> a -> b
    $ (GLfloat -> GLfloat -> (GLfloat, GLfloat))
    -> PName2F -> IO (GLfloat, GLfloat)
    forall p a. GetPName2F p => (GLfloat -> GLfloat -> a) -> p -> IO a
    forall a. (GLfloat -> GLfloat -> a) -> PName2F -> IO a
    getFloat2 (,) PName2F
    GetSmoothPointSizeRange

    -- | The antialiased point size granularity, i.e. the size difference between
    -- supported sizes.

    smoothPointSizeGranularity :: GettableStateVar GLfloat
    smoothPointSizeGranularity :: IO GLfloat
    smoothPointSizeGranularity =
       IO GLfloat -> IO GLfloat
    forall a. IO a -> IO a
    makeGettableStateVar (IO GLfloat -> IO GLfloat) -> IO GLfloat -> IO GLfloat
    forall a b. (a -> b) -> a -> b
    $ (GLfloat -> GLfloat) -> PName1F -> IO GLfloat
    forall p a. GetPName1F p => (GLfloat -> a) -> p -> IO a
    forall a. (GLfloat -> a) -> PName1F -> IO a
    getFloat1 GLfloat -> GLfloat
    forall a. a -> a
    id PName1F
    GetSmoothPointSizeGranularity