packages feed

yx 0.0.3.0 → 0.0.4.0

raw patch · 3 files changed

+64/−20 lines, 3 filesdep +mtldep ~arraydep ~bytestringdep ~hspecPVP ok

version bump matches the API change (PVP)

Dependencies added: mtl

Dependency ranges changed: array, bytestring, hspec

API changes (from Hackage documentation)

+ Data.Geometry.YX: arrayBox :: IArray a e => a YX e -> Maybe Box
+ Data.Geometry.YX: boxNeighbors4 :: Box -> YX -> [YX]
+ Data.Geometry.YX: boxNeighbors8 :: Box -> YX -> [YX]
+ Data.Geometry.YX: byteStringToArrayM :: (IArray a e, MonadError String m) => (YX -> Char -> m e) -> ByteString -> m (a YX e)

Files

src/Data/Geometry/YX.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} @@ -12,22 +13,24 @@   up, left, right, down,   steps4, steps8,   -- * Box-  Box, box, boundingBox,+  Box, box, arrayBox, boundingBox,   topLeft, bottomRight,   boxHeight, boxWidth,   inBox, boxRange, boxRows, boxIntersection,+  boxNeighbors4, boxNeighbors8,   -- * Transformations   Center(..), Direction(..), rotate,   Axis(..), mirror,   -- * Serialization-  byteStringToArray, arrayToByteString+  byteStringToArray, byteStringToArrayM, arrayToByteString ) where  import Prelude hiding (lines)  import Algebra.Lattice (Lattice(..), joinLeq, meetLeq)+import Control.Monad.Except (MonadError, throwError) import Data.Array.IArray (IArray)-import qualified Data.Array.IArray as Array+import qualified Data.Array.IArray as IArray import Data.ByteString (ByteString) import qualified Data.ByteString.Char8 as BS import Data.Foldable (foldl')@@ -107,6 +110,10 @@     then Just $ Box yx0 yx1     else Nothing +-- | Returns the box corresponding to an array, or 'Nothing' if the array is empty.+arrayBox :: IArray a e => a YX e -> Maybe Box+arrayBox = uncurry box . IArray.bounds+ -- | Returns the smallest 'Box' containing all input coordinates. boundingBox :: Foldable f => f YX -> Maybe Box boundingBox = foldl' go Nothing where@@ -141,6 +148,21 @@ boxRows :: Box -> [[YX]] boxRows (Box tl br) = groupBy (\(YX y1 _) (YX y2 _) -> y1 == y2) $ Ix.range (tl, br) +boxNeighbors :: [YX] -> Box -> YX -> [YX]+boxNeighbors steps b yx = filter (`inBox` b) $ fmap (+ yx) steps++-- | Returns 4 neighbors of YX filtered to members of the box.+--+-- @since 0.0.4.0+boxNeighbors4 :: Box -> YX -> [YX]+boxNeighbors4 = boxNeighbors steps4++-- | Returns 8 neighbors of YX filtered to members of the box.+--+-- @since 0.0.4.0+boxNeighbors8 :: Box -> YX -> [YX]+boxNeighbors8 = boxNeighbors steps8+ -- | Intersects two boxes. boxIntersection :: Box -> Box -> Maybe Box boxIntersection (Box tl0 br0) (Box tl1 br1) =@@ -181,23 +203,29 @@ mirror (LeftOfColumn x0) yx = mirror (AtColumn x0) yx + left mirror (AtColumn x0) (YX y1 x1) = YX y1 (2 * x0 - x1) --- | Parses a newline delimited bytestring into an array.-byteStringToArray :: (IArray a e) => (Char -> Maybe e) -> ByteString -> Either String (a YX e)-byteStringToArray f bs = shape (BS.split '\n' bs) (-1) >>= materialize bs where-  shape [] (YX y0 x0) = Right (YX y0 (max x0 0))-  shape (row : rows) yx@(YX y0 x0)-    | null rows && BS.null row = shape [] yx -- Empty last row.+-- | Parses a newline delimited bytestring into an array in an effectful way.+--+-- @since 0.0.4.0+byteStringToArrayM :: (IArray a e, MonadError String m) => (YX -> Char -> m e) -> ByteString -> m (a YX e)+byteStringToArrayM f bs = shape (BS.split '\n' bs) (-1) >>= materialize bs where+  shape [] (YX y0 x0) = pure (YX y0 (max x0 0))+  shape rows@(row : rows') yx@(YX y0 x0)+    | null $ filter (not . BS.null) rows = shape [] yx -- Empty trailing rows.     | otherwise = let x1 = BS.length row - 1                   in if x1 /= x0 && x0 >= 0-                    then Left $ "bad row lengths: " ++ show x0 ++ ", " ++ show x1-                    else shape rows (YX (y0 + 1) x1)-  materialize bs' yx = Array.listArray (0, yx) <$> elems bs'-  elems = sequenceA . fmap parse . filter (/= '\n') . BS.unpack-  parse c = case f c of+                    then throwError $ "bad row lengths: " ++ show x0 ++ ", " ++ show x1+                    else shape rows' (YX (y0 + 1) x1)+  materialize bs' yx = let t = (0, yx) in IArray.listArray t <$> elems (Ix.range t) bs'+  elems yxs bs' = sequenceA $ fmap (uncurry f) $ zip yxs (filter (/= '\n') $ BS.unpack bs')++-- | Parses a newline delimited bytestring into an array.+byteStringToArray :: (IArray a e) => (Char -> Maybe e) -> ByteString -> Either String (a YX e)+byteStringToArray f bs = byteStringToArrayM (const toElem) bs where+  toElem c = case f c of     Just e -> Right e     Nothing -> Left $ "unknown char: " ++ show c  -- | Serializes an array into a bytestring. This function is the reverse of 'byteStringToArray'. arrayToByteString :: (IArray a e) => (e -> Char) -> a YX e -> ByteString arrayToByteString f arr = BS.intercalate "\n" lines where-  lines = fmap (BS.pack . fmap (f . (arr Array.!))) . boxRows . uncurry Box . Array.bounds $ arr+  lines = fmap (BS.pack . fmap (f . (arr IArray.!))) . boxRows . uncurry Box . IArray.bounds $ arr
test/Spec.hs view
@@ -1,7 +1,11 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-}  import Data.Geometry.YX +import Control.Monad.Except (MonadError, runExcept, throwError)+import Control.Monad.State.Strict (runStateT, put) import qualified Data.Array.IArray as Array import Data.Char (digitToInt) import Data.Either (isLeft)@@ -32,6 +36,16 @@       mirror (AboveRow 0) (YX 0 5) `shouldBe` YX (-1) 5    describe "bytestring conversions" $ do+    it "should support state" $ do+      let+        parse yx = \case+          'x' -> put (Just yx) >> pure True+          '.' -> pure True+          '#' -> pure False+          _ -> throwError "bad char"+        got = runExcept $ runStateT (byteStringToArrayM parse ".x\n.#\n") Nothing+        wantArr = Array.listArray (0, 1) [True, True, True, False] :: Array.Array YX Bool+      got `shouldBe` Right (wantArr, Just (YX 0 1))     it "should parse a simple square case" $ do       let         got = byteStringToArray (Just . digitToInt) "12\n34\n"
yx.cabal view
@@ -1,5 +1,5 @@ name:                yx-version:             0.0.3.0+version:             0.0.4.0 synopsis:            Row-major coordinates description:         A minimal library for handling 2D coordinates. homepage:            https://github.com/mtth/yx@@ -18,8 +18,9 @@   exposed-modules:     Data.Geometry.YX   build-depends:       base >= 4.8 && < 5                      , array >= 0.5-                     , bytestring >= 0.10+                     , bytestring                      , lattices >= 2.0+                     , mtl >= 2.2   default-language:    Haskell2010   ghc-options:         -Wall @@ -31,9 +32,10 @@   ghc-options: -threaded -rtsopts -with-rtsopts=-N   build-depends:       base >=4.8 && <5-    , array >= 0.5-    , bytestring >= 0.10-    , hspec >=2.7+    , array+    , bytestring+    , hspec+    , mtl     , yx   default-language: Haskell2010