{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE CPP #-} module Text.AsciiDiagram.DiagramCleaner ( isShapePossible ) where #if !MIN_VERSION_base(4,8,0) import Data.Monoid( mempty ) import Control.Applicative( Applicative, (<*>), (<$>) ) #endif import Control.Applicative( liftA2 ) import Data.List( tails ) import Text.AsciiDiagram.Geometry import Linear( V2( V2 ) , (^-^) ) compareDirections :: Applicative f => (Int -> Int -> Bool) -> f Int -> f Int -> f Bool compareDirections f = liftA2 diffSign where diffSign 0 0 = True diffSign aa bb = f aa bb checkRoundedCorners :: (Int -> Int -> Bool) -> Segment -> Point -> Point -> Segment -> Bool checkRoundedCorners f s1 ap1 ap2 s2 = okX && okY where V2 dirX dirY = ap2 ^-^ ap1 fromS1 = _segEnd s1 ^-^ ap1 fromS2 = _segStart s2 ^-^ ap2 signDirs = signum <$> V2 dirY dirX V2 okX okY = (&&) <$> (compareDirections f signDirs (signum <$> fromS1)) <*> (compareDirections f signDirs (signum <$> fromS2)) checkClosedShape :: Shape -> Bool checkClosedShape shape = all checkClosed elements where elements = (++ shapeElements shape) <$> tails (shapeElements shape) checkClosed :: [ShapeElement] -> Bool -- dir fromS1 fromS1 -- ---> -----> <---- ^ 2 \--- 3 -- /\ | 1 /---- 0 0 ----/ 1 | | \ -- 1/ \2 dir | / / | dir dir | / -- | | | \ \ | | 1 /--- 0 -- 0| |3 v 2 \---- 3 3 ----\ 2 v -- -----> <---- -- fromS2 fromS2 -- -- OK OK BAD BAD checkClosed ( ShapeSegment s1 : ShapeAnchor ap1 AnchorFirstDiag -- '/' : ShapeAnchor ap2 AnchorSecondDiag -- '\' : ShapeSegment s2 : _) = checkRoundedCorners (==) s1 ap1 ap2 s2 -- dir fromS1 fromS1 -- ---> -----> <---- ^ 1 \--- 0 -- /\ | 2 /---- 3 3 ----/ 2 | | \ -- 2/ \1 dir | / / | dir dir | / -- | | | \ \ | | 2 /--- 3 -- 3| |0 v 1 \---- 0 0 ----\ 1 v -- -----> <---- -- fromS2 fromS2 -- -- OK OK BAD BAD checkClosed ( ShapeSegment s1 : ShapeAnchor ap1 AnchorSecondDiag -- '\' : ShapeAnchor ap2 AnchorFirstDiag -- '/' : ShapeSegment s2 : _) = checkRoundedCorners (/=) s1 ap1 ap2 s2 checkClosed ( ShapeAnchor _ AnchorFirstDiag : ShapeAnchor _ AnchorSecondDiag : ShapeAnchor _ AnchorFirstDiag : ShapeAnchor _ AnchorSecondDiag : _) = False checkClosed ( ShapeAnchor _ AnchorSecondDiag : ShapeAnchor _ AnchorFirstDiag : ShapeAnchor _ AnchorSecondDiag : ShapeAnchor _ AnchorFirstDiag : _) = False checkClosed _ = True isBullet :: ShapeElement -> Bool isBullet (ShapeAnchor _ AnchorBullet) = True isBullet _ = False checkOpened :: [ShapeElement] -> Bool checkOpened [ ShapeAnchor _ AnchorFirstDiag , ShapeAnchor _ AnchorSecondDiag] = False checkOpened [ ShapeAnchor _ AnchorSecondDiag , ShapeAnchor _ AnchorFirstDiag] = False checkOpened (all isBullet -> True) = False checkOpened ( ShapeAnchor ap1 AnchorFirstDiag -- '/' : ShapeAnchor ap2 AnchorSecondDiag -- '\' : ShapeSegment s2 : _) = checkRoundedCorners (==) s1 ap1 ap2 s2 where s1 = mempty { _segEnd = ap1 } checkOpened ( ShapeAnchor ap1 AnchorSecondDiag -- '\' : ShapeAnchor ap2 AnchorFirstDiag -- '/' : ShapeSegment s2 : _) = checkRoundedCorners (/=) s1 ap1 ap2 s2 where s1 = mempty { _segEnd = ap1 } checkOpened _ = True checkOpenedShape :: Shape -> Bool checkOpenedShape = checkOpened . shapeElements isShapePossible :: Shape -> Bool isShapePossible shape | shapeIsClosed shape = checkClosedShape shape | otherwise = checkOpenedShape shape