content: {-#LANGUAGE TypeFamilies#-}
  module CV.Fitting where
  
  import CV.Bindings.Fittings
  import CV.Bindings.Types
  import CV.Matrix
  import CV.Image(getSize)
  import Control.Applicative
  import Foreign.C.Types
  import Foreign.Marshal.Alloc
  import Foreign.Marshal.Array
  import Foreign.Marshal.Utils
  import Foreign.Ptr
  import Foreign.Storable
  import System.IO.Unsafe
  import Utils.GeometryClass
  
  data Ellipse = Ellipse {center::(Float,Float)
                         ,width,height::Float
                         ,angle :: Float} deriving (Eq,Show)
  
  -- | Given a (1,n) or (n,1) matrix of points, calculate
  -- (in the least squares sense) the best ellipse around the
  -- points
  fitEllipse :: Matrix (Float,Float) -> Ellipse
  fitEllipse pts = unsafePerformIO $
     withMatPtr pts $ \cMat ->
     alloca $ \result -> do
             c'wrapFitEllipse
              (castPtr cMat)
              result
             C'CvBox2D (C'CvPoint2D32f x y)
                       (C'CvSize2D32f w h)
                       a <- peek result
             return (Ellipse (realToFrac x,realToFrac y)
                             (realToFrac w)
                             (realToFrac h)
                             (realToFrac a))
  
  -- | Fit a line to set of points.
  fitLine2D :: Dist -> Double -> Double -> Double
               -> Matrix (Float,Float)
               -> ((Float,Float),(Float,Float))
  fitLine2D dist_type param reps aeps pts = unsafePerformIO $
     withMatPtr pts $ \cMat ->
     allocaArray 4 $ \result -> do
             c'cvFitLine
              (castPtr cMat)
              (toNum dist_type) param reps aeps result
             [x,y,dx,dy] <- peekArray 4 result
             return ((x,y),(dx,dy))
  
  -- | Fit a minimum area rectangle over a set of points
  minAreaRect :: Matrix (Float,Float) -> C'CvBox2D
  minAreaRect pts = unsafePerformIO $
     withMatPtr pts $ \cMat ->
     with (C'CvBox2D (C'CvPoint2D32f 0 0) (C'CvSize2D32f 0 0) 0) $ \result -> do
             c'wrapMinAreaRect2 (castPtr cMat) nullPtr result
             peek result
  
  -- | Calculate the minimum axis-aligned bounding rectangle of given points.
  boundingRect :: Matrix (Float,Float) -> C'CvRect
  boundingRect pts = unsafePerformIO $
     withMatPtr pts $ \cMat ->
     with (C'CvRect 0 0 0 0) $ \result -> do
             c'wrapBoundingRect (castPtr cMat) 0 result
             peek result
  
  -- | Calculate the minimum enclosing circle of a point set.
  boundingCircle :: (ELP a ~ Double, Point2D a) => Matrix (Float,Float) -> (a, Double)
  boundingCircle pts = unsafePerformIO $
     withMatPtr pts $ \cMat ->
     with 0         $ \cRadius ->
     with (C'CvPoint2D32f 0 0 ) $ \result -> do
             c'cvMinEnclosingCircle (castPtr cMat) result cRadius
             (,) <$> (convertPt <$> peek result) <*> (realToFrac <$> peek cRadius)
  
  -- | Calculcate the clockwise convex hull of a point set
  convexHull :: Matrix (Float,Float) -> Matrix (Float,Float)
  convexHull pts = do
   unsafePerformIO $ do
       res <- create (getSize pts) :: IO (Matrix (Float,Float))
       withMatPtr pts $ \cMat ->
        withMatPtr res $ \cRes ->
         withNewMemory  $ \ptr_mem -> do
          with (C'CvPoint2D32f 0 0 ) $ \result -> do
               c'cvConvexHull2 (castPtr cMat) (castPtr cRes) c'CV_CLOCKWISE 1
               return res
  
  -- | Calculate convexity defects of a contour.
  convexityDefects :: Matrix (Int,Int) -> [(C'CvPoint, C'CvPoint, C'CvPoint,CFloat)]
  convexityDefects pts = unsafePerformIO $
       withMatPtr pts $ \cMat ->
       withNewMemory  $ \ptr_mem -> do
        ptr_hull_seq <- c'cvConvexHull2 (castPtr cMat) (castPtr ptr_mem) c'CV_CLOCKWISE 0
        ptr_seq <- c'cvConvexityDefects (castPtr cMat) (castPtr ptr_hull_seq) (castPtr ptr_mem)
        pts <- cvSeqToList ptr_seq
        sequence [(,,,) <$> peek (c'CvConvexityDefect'start x )
                        <*> peek (c'CvConvexityDefect'end x )
                        <*> peek (c'CvConvexityDefect'depth_point x )
                        <*> return (c'CvConvexityDefect'depth x)
                 | x <- pts ]
  
  