packages feed

KdTree (empty) → 0.1

raw patch · 6 files changed

+248/−0 lines, 6 filesdep +QuickCheckdep +basesetup-changed

Dependencies added: QuickCheck, base

Files

+ Data/Trees/KdTree.hs view
@@ -0,0 +1,125 @@+module Data.Trees.KdTree where++-- Haskell implementation of http://en.wikipedia.org/wiki/K-d_tree+-- by Issac Trotts++import Data.Maybe++import qualified Data.Foldable as F+import qualified Data.List as L+import Test.QuickCheck++class Point p where+      -- |dimension returns the number of coordinates of a point.+      dimension :: p -> Int++      -- |coord gets the k'th coordinate, starting from 0.+      coord :: Int -> p -> Double++      -- |dist2 returns the squared distance between two points.+      dist2 :: p -> p -> Double+      dist2 a b = sum . map diff2 $ [0..dimension a - 1]+	where diff2 i = (coord i a - coord i b)^2++-- |compareDistance p a b  compares the distances of a and b to p.+compareDistance :: (Point p) => p -> p -> p -> Ordering+compareDistance p a b = dist2 p a `compare` dist2 p b++data Point3d = Point3d { p3x :: Double, p3y :: Double, p3z :: Double }+    deriving (Eq, Ord, Show)++instance Point Point3d where+    dimension _ = 3++    coord 0 p = p3x p+    coord 1 p = p3y p+    coord 2 p = p3z p+++data KdTree point = KdNode { kdLeft :: KdTree point,+			     kdPoint :: point,+                             kdRight :: KdTree point,+			     kdAxis :: Int }+                  | KdEmpty+     deriving (Eq, Ord, Show)++instance Functor KdTree where+    fmap _ KdEmpty = KdEmpty+    fmap f (KdNode l x r axis) = KdNode (fmap f l) (f x) (fmap f r) axis++instance F.Foldable KdTree where+    foldr f init KdEmpty = init+    foldr f init (KdNode l x r _) = F.foldr f init3 l+	where 	init3 = f x init2+		init2 = F.foldr f init r++fromList :: Point p => [p] -> KdTree p+fromList points = fromListWithDepth points 0++-- Select axis based on depth so that axis cycles through all valid values+fromListWithDepth :: Point p => [p] -> Int -> KdTree p+fromListWithDepth [] _ = KdEmpty+fromListWithDepth points depth = node+    where   axis = axisFromDepth (head points) depth++	    -- Sort point list and choose median as pivot element+	    sortedPoints =+		L.sortBy (\a b -> coord axis a `compare` coord axis b) points+	    medianIndex = length sortedPoints `div` 2+	+	    -- Create node and construct subtrees+	    node = KdNode { kdLeft = fromListWithDepth (take medianIndex sortedPoints) (depth+1),+			    kdPoint = sortedPoints !! medianIndex,+			    kdRight = fromListWithDepth (drop (medianIndex+1) sortedPoints) (depth+1),+			    kdAxis = axis }++axisFromDepth :: Point p => p -> Int -> Int+axisFromDepth p depth = depth `mod` k+    where k = dimension p++toList :: KdTree p -> [p]+toList t = F.foldr (:) [] t++subtrees :: KdTree p -> [KdTree p]+subtrees KdEmpty = [KdEmpty]+subtrees t@(KdNode l x r axis) = subtrees l ++ [t] ++ subtrees r++nearestNeighbor :: Point p => KdTree p -> p -> Maybe p+nearestNeighbor KdEmpty probe = Nothing+nearestNeighbor (KdNode KdEmpty p KdEmpty _) probe = Just p+nearestNeighbor (KdNode l p r axis) probe =+    if xProbe <= xp then doStuff l r else doStuff r l+    where xProbe = coord axis probe+	  xp = coord axis p+          doStuff tree1 tree2 =+		let candidates1 = case nearestNeighbor tree1 probe of+				    Nothing -> [p]+				    Just best1 -> [best1, p]+		    sphereIntersectsPlane = (xProbe - xp)^2 <= dist2 probe p+		    candidates2 = if sphereIntersectsPlane+				    then candidates1 ++ maybeToList (nearestNeighbor tree2 probe)+				    else candidates1 in+		Just . L.minimumBy (compareDistance probe) $ candidates2++-- |invariant tells whether the KD tree property holds for a given tree and+-- all its subtrees.+-- Specifically, it tests that all points in the left subtree lie to the left+-- of the plane, p is on the plane, and all points in the right subtree lie to+-- the right.+invariant :: Point p => KdTree p -> Bool+invariant KdEmpty = True+invariant (KdNode l p r axis) = leftIsGood && rightIsGood+    where x = coord axis p+	  leftIsGood = all ((<= x) . coord axis) (toList l)+	  rightIsGood = all ((>= x) . coord axis) (toList r)++invariant' :: Point p => KdTree p -> Bool+invariant' = all invariant . subtrees++instance Arbitrary Point3d where+    arbitrary = do+	x <- arbitrary+	y <- arbitrary+	z <- arbitrary+	return (Point3d x y z)+
+ KdTree.cabal view
@@ -0,0 +1,35 @@+Name:                KdTree++-- The package version. See the Haskell package versioning policy+-- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for+-- standards guiding when and how versions should be incremented.+Version:             0.1+Synopsis:            KdTree, for efficient search in K-dimensional point clouds.+Description:         +    This is a simple library for k-d trees in Haskell. It enables efficient+    searching through collections of points in O(log N) time on average,+    using the nearestNeighbor function.++Homepage:            https://github.com/ijt/kdtree+License:             BSD3+License-file:        LICENSE+Author:              Issac Trotts+Maintainer:          issac.trotts@gmail.com+Copyright:           Copyright 2011, Issac Trotts+Category:            Graphics+Build-type:          Simple+Cabal-version:       >=1.6+Extra-source-files:  README++source-repository head+  type:     git+  location: git@github.com:ijt/kdtree.git++Library+  Exposed-modules: Data.Trees.KdTree+  Build-depends: base < 5+  +Executable KdTreeTest+  Main-is: KdTreeTest.hs+  Build-depends: QuickCheck+
+ KdTreeTest.hs view
@@ -0,0 +1,35 @@+{-# LANGUAGE TemplateHaskell #-}++module Main where++import Data.Maybe+import qualified Data.List as L++import Test.QuickCheck+import Test.QuickCheck.All++import qualified Data.Trees.KdTree as Kd++prop_invariant :: [Kd.Point3d] -> Bool+prop_invariant points = Kd.invariant' . Kd.fromList $ points++prop_samePoints :: [Kd.Point3d] -> Bool+prop_samePoints points = L.sort points == (L.sort . Kd.toList . Kd.fromList $ points)++prop_nearestNeighbor :: [Kd.Point3d] -> Kd.Point3d -> Bool+prop_nearestNeighbor points probe =+    Kd.nearestNeighbor tree probe == bruteNearestNeighbor points probe+    where tree = Kd.fromList points++prop_pointsAreClosestToThemselves :: [Kd.Point3d] -> Bool+prop_pointsAreClosestToThemselves points =+    map Just points == map (Kd.nearestNeighbor tree) points+    where tree = Kd.fromList points++bruteNearestNeighbor :: [Kd.Point3d] -> Kd.Point3d -> Maybe Kd.Point3d+bruteNearestNeighbor [] _ = Nothing+bruteNearestNeighbor points probe =+    Just . head . L.sortBy (Kd.compareDistance probe) $ points++main = $quickCheckAll+
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c)2011, Issac Trotts++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Issac Trotts nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README view
@@ -0,0 +1,21 @@+This is a simple library for k-d trees in Haskell, based on the algorithms+at http://en.wikipedia.org/wiki/K-d_tree.++It enables efficient searching through collections of points in O(log N) time+for randomly distributed points, using the nearestNeighbor function.++Here is an example of an interactive session using this module:++[ ~/haskell/KdTree ] ghci+GHCi, version 7.0.3: http://www.haskell.org/ghc/  :? for help+...+Prelude> :m Data.Trees.KdTree +Prelude Data.Trees.KdTree> import Test.QuickCheck+Prelude Data.Trees.KdTree Test.QuickCheck> points <- sample' arbitrary :: IO [Point3d]+...+Prelude Data.Trees.KdTree Test.QuickCheck> let tree = fromList points+Prelude Data.Trees.KdTree Test.QuickCheck> nearestNeighbor tree (head points)+Just (Point3d {p3x = 0.0, p3y = 0.0, p3z = 0.0})+Prelude Data.Trees.KdTree Test.QuickCheck> head points+Point3d {p3x = 0.0, p3y = 0.0, p3z = 0.0}+
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain