packages feed

svg-icons-0.1.0.3: src/Core/Utils.hs

{-# LANGUAGE     OverloadedStrings       #-}



module Core.Utils 
  ( evenOddSplit
  , addXmlns
  , (.:)
  , distance
  , horizontalMirrorMatrix
  , verticalMirrorMatrix
  , frame
  , cleanDecimals
  ) where

import Data.Char
import           Text.Blaze.Svg11 ((!))
import           Text.Blaze.Svg11 as S
import           Text.Blaze.Svg11.Attributes as A



{- |
Splits a list @xs@ into two lists:

* The first one contains all odd positioned elements of @xs@
* The second one contains all even positioned elements of @xs@
-}
evenOddSplit :: [a] -> ([a], [a])
evenOddSplit [] = ([], [])
evenOddSplit (x:xs) = (x:o, e)
  where (e,o) = evenOddSplit xs



{- |
Takes some `Svg` entity and adds two attributes:

* @xmlns="http://www.w3.org/2000/svg"@
* @xmlns:xlink="http://www.w3.org/1999/xlink"@
-}
addXmlns :: Svg -> Svg
addXmlns svg =
  svg
    ! customAttribute "xmlns" "http://www.w3.org/2000/svg"
    ! customAttribute "xmlns:xlink" "http://www.w3.org/1999/xlink"



{- |
Handy operator that converts a `Float` number
into an `AttributeValue` and feeds it to the `Attribute` function.
Example:

>S.path 
>  ! (A.strokeWidth .: 0.1) 
-}
infixl 5 .:
(.:) :: (AttributeValue -> Attribute ) -> Float -> Attribute
f .: x = f $ S.toValue x



{- |
Euclidian distance between two points.
-}
distance :: (Float, Float) -> (Float, Float) -> Float
distance (ax,ay) (bx,by) =
  sqrt $ (bx - ax)**2 + (by - ay)**2



{- |
Matrix for the horizontal symmetry __respect to the axis @x=0@__.
Use it with the transform `Attribute`:

>S.path
>  ! A.transform horizontalMirrorMatrix
-}
horizontalMirrorMatrix :: AttributeValue
horizontalMirrorMatrix =
  matrix (-1) 0 0 1 0 0



{- |
Matrix for the vertical symmetry __respect to the axis @y=0@__.
Use it with the transform `Attribute`:

>S.path
>  ! A.transform (verticalMirrorMatrix <> rotateAround 45 0 0)
-}
verticalMirrorMatrix :: AttributeValue
verticalMirrorMatrix =
  matrix 1 0 0 (-1) 0 0



{- |
`frame` is mainly used for testing purposes.

Takes the 4 numbers of the viewbox @(x0, y0, width, height)@
and returns a path with a very thin stroke which connects all 
consecutive corners of the viewbox and also connects opposite
middle points of the sides of the viewbox.
-}
frame :: Float -> Float -> Float -> Float -> S.Svg
frame x y w h =
    S.path
      ! A.fill "none"
      ! A.stroke "black"
      ! A.strokeWidth "0.002"
      ! A.d frameDirs
  where
    frameDirs = mkPath $ do
      m   x       y
      l   x      (y + h)    
      l  (x + w) (y + h)
      l  (x + w)  y
      S.z
      m  (x + w/2)  y
      l  (x + w/2) (y + h)
      m   x        (y + h/2)
      l  (x + w)   (y + h/2)


-- Takes a number n and a string s, 
-- and returns a string equal to s except that every decimal
-- number inside s will have its decimal part capped at n digits
{- |
Please ignore this function.
-}
cleanDecimals :: Int -> String -> String
cleanDecimals n s = 
    f [] [] s
  where
    f _ acc [] = reverse acc
    f aux acc (c:cs) = 
      if c == '.'
        then f "." acc cs
        else if aux == []
          then f [] (c : acc) cs
          else if (not $ isDigit c)
            then f [] (c : aux ++ acc) cs
            else if (length aux < n)
              then f (c : aux) acc cs
              else f aux acc cs