asciidiagram-1.0: src/Text/AsciiDiagram/DiagramCleaner.hs
{-# 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