packages feed

dph-examples-0.5.1.1: spectral/QuickHull/vector/QuickHullVector.hs

{-# LANGUAGE BangPatterns, PatternGuards #-}

module QuickHullVector
	(quickHull)
where
import Points2D.Types
import Data.Function
import Data.Vector.Unboxed		as V
import Data.Vector.Unboxed.Mutable	as MV
import Data.Vector.Unboxed		(Vector)
import qualified Data.Vector.Generic	as G
import System.IO.Unsafe



distance :: Point -> Point -> Point -> Double
{-# INLINE distance #-}
distance (x1, y1) (x2, y2) (xo, yo)
  = (x1-xo) * (y2 - yo) - (y1 - yo) * (x2 - xo)


hsplit :: Vector Point -> Point -> Point -> Vector Point
{-# INLINE hsplit #-}
hsplit !points !p1@(!p1X, !p1Y) !p2@(!p2X, !p2Y)
 = let !packed	= packPoints points p1X p1Y p2X p2Y
   in  if V.length packed == 1
	 then V.singleton p1
	 else 	let pm		= packed `V.unsafeIndex` (V.length packed - 1)
		    packed'	= V.unsafeSlice 0 (V.length packed - 1) packed
		in  hsplit packed' p1 pm V.++ hsplit packed' pm p2


packPoints :: Vector Point -> Double -> Double -> Double -> Double -> Vector Point
{-# INLINE packPoints #-}
packPoints !points !p1X !p1Y !p2X !p2Y
 = G.create 
 $ do	packed	<- MV.new (V.length points + 1)
	
	-- stash the furthest point on the end of the returned vector.	
	let fill !pMax !distMax !ixPoints !ixPacked
		| ixPoints >= V.length points	
		= do	MV.unsafeWrite packed ixPacked pMax
			return $ MV.unsafeSlice 0 (ixPacked + 1) packed

		| p	<- points `V.unsafeIndex` ixPoints
		, d	<- distance (p1X, p1Y) (p2X, p2Y) p
		, d > 0
		= do	MV.unsafeWrite packed ixPacked p
			if d > distMax
			 then	fill p    d       (ixPoints + 1) (ixPacked + 1)
			 else	fill pMax distMax (ixPoints + 1) (ixPacked + 1)
			
		| otherwise
		= fill pMax distMax (ixPoints + 1) ixPacked
			
	fill (0, 0) 0 0 0


quickHull :: Vector Point -> Vector Point
quickHull !points
  	| V.length points == 0	= points

	| (minx, maxx) 		<- minmax points
	= hsplit points minx maxx V.++ hsplit points maxx minx


minmax :: Vector Point -> (Point, Point)
{-# INLINE minmax #-}
minmax !vec
 = go first first 0
 where	first	= vec V.! 0

	go pMin@(!minX, !minY) pMax@(!maxX, !maxY) !ix
	  | ix >= V.length vec	= (pMin, pMax)

	  | (x, y)	<- vec `V.unsafeIndex` ix
	  = if       x < minX then go (x, y) pMax   (ix + 1)
	    else if  x > maxX then go pMin   (x, y) (ix + 1)
	    else go pMin pMax (ix + 1)