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

module Graphics.Rendering.OpenGL.GL.LineSegments (
   -- * Line Rasterization
   lineWidth,

   -- * Line Stipple
   lineStipple,

   -- * Line Antialiasing
  lineSmooth,

   -- * Implementation-Dependent Limits
   aliasedLineWidthRange, smoothLineWidthRange, smoothLineWidthGranularity
) where

import Control.Monad
import Data.StateVar
import Graphics.Rendering.OpenGL.GL.Capability
import Graphics.Rendering.OpenGL.GL.QueryUtils
import Graphics.GL

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

-- | 'lineWidth' contains the rasterized width of both aliased and antialiased
-- lines. The initial value is 1. Using a line width other than 1 has different
-- effects, depending on whether line antialiasing is enabled (see
-- 'lineSmooth'). Line antialiasing is initially disabled.
--
-- If line antialiasing is disabled, the actual width is determined by rounding
-- the supplied width to the nearest integer. (If the rounding results in the
-- value 0, it is as if the line width were 1.) If /delta x/ >= /delta y/, /i/
-- pixels are filled in each column that is rasterized, where /i/ is the
-- rounded value of 'lineWidth'. Otherwise, /i/ pixels are filled in each row
-- that is rasterized.
--
-- If antialiasing is enabled, line rasterization produces a fragment for each
-- pixel square that intersects the region lying within the rectangle having
-- width equal to the current line width, length equal to the actual length of
-- the line, and centered on the mathematical line segment. The coverage value
-- for each fragment is the window coordinate area of the intersection of the
-- rectangular region with the corresponding pixel square. This value is saved
-- and used in the final rasterization step.
--
-- Not all widths can be supported when line antialiasing is enabled. If an
-- unsupported width is requested, the nearest supported width is used. Only
-- width 1 is guaranteed to be supported; others depend on the implementation.
--  Likewise, there is a range for aliased line widths as well. To query the
-- range of supported widths of antialiased lines and the size difference
-- between supported widths within the range, query 'smoothLineWidthRange' and
-- 'smoothLineWidthGranularity', respectively. For aliased lines, query the
-- supported range with 'aliasedLineWidthRange'.
--
-- The line width specified when 'lineWidth' is set is always returned when it
-- is queried. Clamping and rounding for aliased and antialiased lines have no
-- effect on the specified value.
--
-- A non-antialiased line width may be clamped to an implementation-dependent
-- maximum.  Query 'aliasedLineWidthRange' to determine the maximum width.
--
-- An 'Graphics.Rendering.OpenGL.GLU.Errors.InvalidValue' is generated if
-- 'lineWidth' is set to a value less than or equal to zero.
--
-- An 'Graphics.Rendering.OpenGL.GLU.Errors.InvalidOperation' is generated if
-- 'lineWidth' is set during
-- 'Graphics.Rendering.OpenGL.GL.BeginEnd.renderPrimitive'.

lineWidth :: StateVar GLfloat
lineWidth :: StateVar GLfloat
lineWidth = 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
getFloat1 GLfloat -> GLfloat
forall a. a -> a
id PName1F
GetLineWidth) GLfloat -> IO ()
forall (m :: * -> *). MonadIO m => GLfloat -> m ()
glLineWidth

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

-- | Line stippling masks out certain fragments produced by rasterization; those
-- fragments will not be drawn. The masking is achieved by using three
-- parameters: the repeat count (1st element of the 'lineStipple' pair, clamped
-- to the range [ 1 .. 256 ]), the 16-bit line stipple pattern (2nd element),
-- and an integer stipple counter /s/.
--
-- The counter /s/ is reset to 0 at before the first action during
-- 'Graphics.Rendering.OpenGL.GL.BeginEnd.renderPrimitive' is called and before
-- each line segment during
-- 'Graphics.Rendering.OpenGL.GL.BeginEnd.renderPrimitive' is generated. It is
-- incremented after each fragment of a unit width aliased line segment is
-- generated or after each /i/ fragments of an /i/ width line segment are
-- generated. The /i/ fragments associated with count /s/ are masked out if
-- @'Data.Bits.testBit' /pattern/ (( /s/ \/ /factor/ ) /mod/ 16)@ is 'False',
-- otherwise these fragments are sent to the frame buffer. Bit zero of the
-- pattern is the least significant bit, i.e. it is used first.
--
-- Antialiased lines are treated as a sequence of rectangles of height 1 for
-- purposes of stippling. Whether rectangle /s/ is rasterized or not depends on
-- the fragment rule described for aliased lines, counting rectangles rather
-- than groups of fragments.
--
-- The initial value of 'lineStipple' is 'Nothing', i.e. line stippling is
-- disabled.
--
-- An 'Graphics.Rendering.OpenGL.GLU.Errors.InvalidOperation' is generated if
-- 'lineStipple' is set during
-- 'Graphics.Rendering.OpenGL.GL.BeginEnd.renderPrimitive'.

lineStipple :: StateVar (Maybe (GLint, GLushort))
lineStipple :: StateVar (Maybe (GLint, GLushort))
lineStipple =
   IO EnableCap
-> IO (GLint, GLushort)
-> ((GLint, GLushort) -> IO ())
-> StateVar (Maybe (GLint, GLushort))
forall a.
IO EnableCap -> IO a -> (a -> IO ()) -> StateVar (Maybe a)
makeStateVarMaybe
      (EnableCap -> IO EnableCap
forall (m :: * -> *) a. Monad m => a -> m a
return EnableCap
CapLineStipple)
      ((GLint -> GLushort -> (GLint, GLushort))
-> IO GLint -> IO GLushort -> IO (GLint, GLushort)
forall (m :: * -> *) a1 a2 r.
Monad m =>
(a1 -> a2 -> r) -> m a1 -> m a2 -> m r
liftM2 (,) ((GLint -> GLint) -> PName1I -> IO GLint
forall p a. GetPName1I p => (GLint -> a) -> p -> IO a
getInteger1 GLint -> GLint
forall a. a -> a
id PName1I
GetLineStippleRepeat)
                  ((GLint -> GLushort) -> PName1I -> IO GLushort
forall p a. GetPName1I p => (GLint -> a) -> p -> IO a
getInteger1 GLint -> GLushort
forall a b. (Integral a, Num b) => a -> b
fromIntegral PName1I
GetLineStipplePattern))
      ((GLint -> GLushort -> IO ()) -> (GLint, GLushort) -> IO ()
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry GLint -> GLushort -> IO ()
forall (m :: * -> *). MonadIO m => GLint -> GLushort -> m ()
glLineStipple)

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

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

lineSmooth :: StateVar Capability
lineSmooth :: StateVar Capability
lineSmooth = EnableCap -> StateVar Capability
makeCapability EnableCap
CapLineSmooth

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

-- | The smallest and largest supported width of aliased lines.

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

-- | The smallest and largest supported width of antialiased lines.

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

-- | The antialiased line width granularity, i.e. the size difference between
-- supported widths.

smoothLineWidthGranularity :: GettableStateVar GLfloat
smoothLineWidthGranularity :: IO GLfloat
smoothLineWidthGranularity =
   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
getFloat1 GLfloat -> GLfloat
forall a. a -> a
id PName1F
GetSmoothLineWidthGranularity