packages feed

clipper-0.0.1: Algebra/Clipper.hsc

{-# LANGUAGE CPP, ForeignFunctionInterface, EmptyDataDecls #-}
module Algebra.Clipper
(
 ClipType,ctIntersection,ctUnion,ctDifference,ctXor
,PolyType,ptSubject,ptClip
,PolyFillType,pftEvenOdd,pftNonZero
,IntPoint(..)
,Polygon(..)
,Polygons(..)
,(<++>)
,execute
,intersection,(<@>)
,union,(<+>)
,difference,(<->)
,Algebra.Clipper.xor,(<^>)
,polygonArea
,polygonIsClockwise
) where

import Foreign
import Foreign.C.Types
import Data.Int(Int64)
import Data.Word(Word64)
import Data.Monoid
import Control.Applicative((<$>), (<*>))

#include <clipper.hpp>

-- enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
newtype ClipType = ClipType Int
#enum ClipType, ClipType, ctIntersection = ctIntersection, ctUnion = ctUnion, ctDifference = ctDifference, ctXor = ctXor

-- enum PolyType { ptSubject, ptClip };
newtype PolyType = PolyType Int
#enum PolyType, PolyType, ptSubject = ptSubject, ptClip = ptClip

-- enum PolyFillType { pftEvenOdd, pftNonZero };
newtype PolyFillType = PolyFillType Int
#enum PolyFillType, PolyFillType, pftEvenOdd = pftEvenOdd, pftNonZero = pftNonZero

-- struct IntPoint {
--   long64 X;
--   long64 Y;
--   IntPoint(long64 x = 0, long64 y = 0): X(x), Y(y) {};
-- };

data IntPoint = IntPoint
    {
      pointX :: #{type long64}
    , pointY :: #{type long64}
    } deriving Show

-- typedef std::vector< IntPoint > Polygon;
-- typedef std::vector< Polygon > Polygons;

newtype Polygon = Polygon [IntPoint] deriving Show
type PolygonPtr = Ptr Polygon

instance Monoid Polygon where
    mempty = Polygon mempty
    mappend (Polygon x) (Polygon y) = Polygon (x `mappend` y)

instance Storable Polygon where
    sizeOf _ = #{size Polygon}
    alignment _ = alignment (undefined :: CInt)
    peek ptr = do
         numPts <- fromIntegral <$> polygonSize ptr
         Polygon <$> mapM (polygonGetPoint ptr) [0..(numPts-1)]
    poke ptr (Polygon ps) = do
         polygonClear ptr
         mapM_ addPt ps
        where addPt (IntPoint x y) = polygonAddPoint ptr x y

newtype Polygons = Polygons [Polygon] deriving Show
type PolygonsPtr = Ptr Polygons

size (Polygon ps) = fromIntegral $ length ps
sizes (Polygons ps) = fromIntegral $ length ps

instance Monoid Polygons where
    mempty = Polygons mempty
    mappend (Polygons x) (Polygons y) = Polygons (x `mappend` y)

instance Storable Polygons where
    sizeOf _ = #{size Polygons}
    alignment _ = alignment (undefined :: CInt)
    peek ptr = do
         numPolys <- fromIntegral <$> polygonsSize ptr
         polyPtrs <- mapM (polygonsGetPoly ptr) [0..(numPolys-1)]
         Polygons <$> mapM peek polyPtrs
    poke ptr (Polygons ps) = do
         polygonsClear ptr
         mapM_ addPoly ps
        where addPoly poly = polygonNew (size poly) >>= 
                             newForeignPtr polygonFree >>= 
                             flip withForeignPtr (setPoly poly)
              setPoly poly pptr = poke pptr poly >> polygonsAddPoly ptr pptr

a <++> b = a `mappend` b

-- struct ExPolygon {
--   Polygon  outer;
--   Polygons holes;
-- };
-- typedef std::vector< ExPolygon > ExPolygons;

data ExPolygon
type ExPolygonPtr = Ptr ExPolygon

data ExPolygons
type ExPolygonsPtr = Ptr ExPolygons

data Clipper
type ClipperPtr = Ptr Clipper

-- extern "C" {
-- typedef void * polygon;
-- typedef void * polygons;

polygonGetPoint :: PolygonPtr -> Int -> IO IntPoint
polygonGetPoint ptr i = IntPoint <$> polygonGetPointX ptr i' <*> polygonGetPointY ptr i'
    where i' = fromIntegral i

polygonArea :: Polygon -> IO Double
polygonArea poly = do
  fptr <- polygonNew (size poly) >>= 
          newForeignPtr polygonFree
  withForeignPtr fptr (flip poke poly)
  withForeignPtr fptr (flip polygonArea_ 0)

polygonIsClockwise :: Polygon -> IO Bool
polygonIsClockwise poly = do
  fptr <- polygonNew (size poly) >>= 
          newForeignPtr polygonFree
  withForeignPtr fptr (flip poke poly)
  ret <- withForeignPtr fptr (flip polygonIsClockwise_ 0)
  if ret == 0 then return False else return True

execute :: ClipType -> Polygons -> Polygons -> IO Polygons
execute cType sPolys cPolys = clipperNew >>= 
                              newForeignPtr clipperFree >>= 
                              flip withForeignPtr exec_
    where exec_ cPtr = do
            spPtr <- polygonsNew (sizes sPolys) >>= newForeignPtr polygonsFree
            withForeignPtr spPtr (\subptr -> poke subptr sPolys >> 
                                             clipperAddPolygons cPtr subptr ptSubject)
            cpPtr <- polygonsNew (sizes cPolys) >>= newForeignPtr polygonsFree
            withForeignPtr cpPtr (\clpptr -> poke clpptr cPolys >> 
                                             clipperAddPolygons cPtr clpptr ptClip)
            rPtr <- polygonsNew 0 >>= newForeignPtr polygonsFree
            withForeignPtr rPtr (\resPtr -> clipperExecutePolys cPtr cType resPtr)
            withForeignPtr rPtr peek

intersection = execute ctIntersection
a <@> b = a `intersection` b

union = execute ctUnion
a <+> b = a `union` b

difference = execute ctDifference
a <-> b = a `difference` b

xor = execute ctXor
a <^> b = a `Algebra.Clipper.xor` b

--   long64 polygon_getPointX(polygon poly, int i);
foreign import ccall "clipper.hpp polygon_getPointX"
        polygonGetPointX :: PolygonPtr -> #{type int} -> IO #{type long64}

--   long64 polygon_getPointY(polygon poly, int i);
foreign import ccall "clipper.hpp polygon_getPointY"
        polygonGetPointY :: PolygonPtr -> #{type int} -> IO #{type long64}

--   polygon polygon_new(int numPoints);
foreign import ccall "clipper.hpp polygon_new"
        polygonNew :: #{type int} -> IO PolygonPtr

--   void polygon_clear(polygon poly);
foreign import ccall "clipper.hpp polygon_clear"
        polygonClear :: PolygonPtr -> IO ()

--   void polygon_size(polygon poly);
foreign import ccall "clipper.hpp polygon_size"
        polygonSize :: PolygonPtr -> IO CInt

--   void polygon_addPoint(polygon poly, long64 x, long64 y)
foreign import ccall "clipper.hpp polygon_addPoint"
        polygonAddPoint :: PolygonPtr -> #{type long64} -> #{type long64} -> IO ()

--   void polygon_free(polygon poly);
foreign import ccall "clipper.hpp &polygon_free"
        polygonFree :: FunPtr (PolygonPtr -> IO ())

--   int polygon_isClockwise(polygon poly, int useFullInt64Range)
foreign import ccall "clipper.hpp polygon_isClockwise"
        polygonIsClockwise_ :: PolygonPtr -> #{type int} -> IO #{type int}

--   double polygon_getArea(polygon poly, int useFullInt64Range)
foreign import ccall "clipper.hpp polygon_getArea"
        polygonArea_ :: PolygonPtr -> #{type int} -> IO #{type double}

--   polygons polygons_new(int numPolys);
foreign import ccall "clipper.hpp polygons_new"
        polygonsNew :: #{type int}  -> IO PolygonsPtr

--   void polygons_clear(polygons poly);
foreign import ccall "clipper.hpp polygons_clear"
        polygonsClear :: PolygonsPtr -> IO ()

--   void polygons_size(polygons poly);
foreign import ccall "clipper.hpp polygons_size"
        polygonsSize :: PolygonsPtr -> IO CInt

--   void polygons_addPoly(polygons polys, polygon poly);
foreign import ccall "clipper.hpp polygons_addPoly"
        polygonsAddPoly :: PolygonsPtr -> PolygonPtr -> IO ()

--   polygon polygons_getPoly(polygons polys, int i);
foreign import ccall "clipper.hpp polygons_getPoly"
        polygonsGetPoly :: PolygonsPtr -> CInt -> IO PolygonPtr

--   void polygons_free(polygons poly);
foreign import ccall "clipper.hpp &polygons_free"
        polygonsFree :: FunPtr (PolygonsPtr -> IO ())

--   clipper clipper_new();
foreign import ccall "clipper.hpp clipper_new"
        clipperNew :: IO ClipperPtr

--   void clipper_addPolygon(clipper c, polygon poly, PolyType ptype);
foreign import ccall "clipper.hpp clipper_addPolygon"
        clipperAddPolygon :: ClipperPtr -> PolygonPtr -> PolyType -> IO ()

--   void clipper_addPolygons(clipper c, polygons poly, PolyType ptype);
foreign import ccall "clipper.hpp clipper_addPolygons"
        clipperAddPolygons :: ClipperPtr -> PolygonsPtr -> PolyType -> IO ()

--   void clipper_executePoly(clipper c, ClipType ctype, polygons soln);
foreign import ccall "clipper.hpp clipper_executePoly"
        clipperExecutePolys :: ClipperPtr -> ClipType -> PolygonsPtr -> IO ()

--   void clipper_free(clipper c);
foreign import ccall "clipper.hpp &clipper_free"
        clipperFree :: FunPtr (ClipperPtr -> IO ())

-- }