import System import List import Maybe import Text.Printf import IO main = do [pathToQhull,f] <- getArgs s <- readFile f let ls = map (spltAll1 ",") $ lines s let n = length ls let cs = map (unwords . tail ) ls let rs = map head ls -- Write a file suitable for QHull to use: writeFile "tmp0.qh" $ "3" ++"\n"++ show n ++"\n"++ unlines cs vs <- rch pathToQhull 0 cs let rchList = map snd $ sort vs putStr $ unlines $ map show rchList -- Clean up and exit system "rm tmp*" -- Convert Points to Coordinates pts2cs cs (p,ps) = (getCs p,map getCs ps) where getCs p = s2c $ cs!!p s2c s = l2t $ map asDouble $ words s l2t [x,y,z] = (x,y,z) -------------------------------------------------------------------------------- -- Recursive Convex Hulls -- lvs == local vertices -- gvs == global vertices .... -- h == hull coords rch pathToQhull i rs = do let inf = "tmp"++show i ++".qh" let outf = "tmp"++show i ++".off" system $ pathToQhull ++ "qhull o TI "++inf++" TO "++outf -- Parse and process the QHull output ".off" file format -- Start by reading back in the file that was input to QHull a<-readFile inf let la = lines a let cs = drop 2 la -- The use this to read and process the QHull output file b<-readFile outf -- Extract the Vertices from the ".off" file let lb = drop (length la) $ lines b let lvs = nub $ sort $ map asInt $ words $ unlines $ map (unwords . tail . words) lb let gvs = [ (fromJust $ elemIndex (cs!!v) rs,i) | v <- lvs ] -- Map the QHull output vertices back to the original Coordinates let h = [ cs!!v | v <- lvs ] -- The Hull -- Find Coordinates NOT in the Hull let cs' = [ c | c <- cs , notElem c h ] let ln = length cs' -- Write the remaining (non-hull points) to a file of points suitable for QHull to read: writeFile ("tmp"++show (i+1)++".qh") $ "3" ++"\n"++ show ln ++"\n"++ unlines cs' if ln<4 then do -- Stop let vs = [ (fromJust $ elemIndex c rs,i+1) | c <- cs' ] -- Maximum hull number return $ gvs ++ vs else do -- Recurse vs <- rch pathToQhull (i+1) rs -- Maximum hull number return $ gvs ++ vs -------------------------------------------------------------------------------- -- Utils asDouble s = read s :: Double asInt s = read s :: Int spltAll1 p s -- Split a string on a pattern | l==[] = [s] | otherwise = l where l = spltAll p s spltAll p s | l==[s] = [] | otherwise = filter (/=p) l where l = spt p s spt p [] = [[]] spt p s = if i==Nothing then [s] else (take (fromJust i) s) : spt p (drop ((fromJust i)+n) s) where i = findIndex (isPrefixOf p) $ tails s n = length p