module Reanimate.Svg.BoundingBox
  ( boundingBox
  , svgHeight
  , svgWidth
  ) where
import           Control.Arrow             ((***))
import           Control.Lens              ((^.))
import           Data.List                 (foldl')
import           Data.Maybe                (mapMaybe)
import qualified Data.Vector.Unboxed       as V
import qualified Geom2D.CubicBezier.Linear as Bezier
import           Graphics.SvgTree
import           Linear.V2                 (V2 (V2))
import           Linear.Vector             (Additive (zero))
import           Reanimate.Constants       (defaultDPI)
import           Reanimate.Svg.LineCommand (LineCommand (..), toLineCommands)
import qualified Reanimate.Transform       as Transform
boundingBox :: Tree -> (Double, Double, Double, Double)
boundingBox :: Tree -> (Double, Double, Double, Double)
boundingBox Tree
t =
    case Tree -> [RPoint]
svgBoundingPoints Tree
t of
      [] -> (Double
0,Double
0,Double
0,Double
0)
      (V2 Double
x Double
y:[RPoint]
rest) ->
        let (Double
minx, Double
miny, Double
maxx, Double
maxy) = ((Double, Double, Double, Double)
 -> RPoint -> (Double, Double, Double, Double))
-> (Double, Double, Double, Double)
-> [RPoint]
-> (Double, Double, Double, Double)
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (Double, Double, Double, Double)
-> RPoint -> (Double, Double, Double, Double)
forall d. Ord d => (d, d, d, d) -> V2 d -> (d, d, d, d)
worker (Double
x, Double
y, Double
x, Double
y) [RPoint]
rest
        in (Double
minx, Double
miny, Double
maxxDouble -> Double -> Double
forall a. Num a => a -> a -> a
-Double
minx, Double
maxyDouble -> Double -> Double
forall a. Num a => a -> a -> a
-Double
miny)
  where
    worker :: (d, d, d, d) -> V2 d -> (d, d, d, d)
worker (d
minx, d
miny, d
maxx, d
maxy) (V2 d
x d
y) =
      (d -> d -> d
forall a. Ord a => a -> a -> a
min d
minx d
x, d -> d -> d
forall a. Ord a => a -> a -> a
min d
miny d
y, d -> d -> d
forall a. Ord a => a -> a -> a
max d
maxx d
x, d -> d -> d
forall a. Ord a => a -> a -> a
max d
maxy d
y)
svgHeight :: Tree -> Double
svgHeight :: Tree -> Double
svgHeight Tree
t = Double
h
  where
    (Double
_x, Double
_y, Double
_w, Double
h) = Tree -> (Double, Double, Double, Double)
boundingBox Tree
t
svgWidth :: Tree -> Double
svgWidth :: Tree -> Double
svgWidth Tree
t = Double
w
  where
    (Double
_x, Double
_y, Double
w, Double
_h) = Tree -> (Double, Double, Double, Double)
boundingBox Tree
t
linePoints :: [LineCommand] -> [RPoint]
linePoints :: [LineCommand] -> [RPoint]
linePoints = RPoint -> [LineCommand] -> [RPoint]
worker RPoint
forall (f :: * -> *) a. (Additive f, Num a) => f a
zero
  where
    worker :: RPoint -> [LineCommand] -> [RPoint]
worker RPoint
_from [] = []
    worker RPoint
from (LineCommand
x:[LineCommand]
xs) =
      case LineCommand
x of
        LineMove RPoint
to     -> RPoint -> [LineCommand] -> [RPoint]
worker RPoint
to [LineCommand]
xs
        
        LineBezier [RPoint
p] ->
          RPoint
p RPoint -> [RPoint] -> [RPoint]
forall a. a -> [a] -> [a]
: RPoint -> [LineCommand] -> [RPoint]
worker RPoint
p [LineCommand]
xs
        LineBezier [RPoint]
ctrl -> 
          let bezier :: AnyBezier Double
bezier = Vector RPoint -> AnyBezier Double
forall a. Vector (V2 a) -> AnyBezier a
Bezier.AnyBezier ([RPoint] -> Vector RPoint
forall a. Unbox a => [a] -> Vector a
V.fromList (RPoint
fromRPoint -> [RPoint] -> [RPoint]
forall a. a -> [a] -> [a]
:[RPoint]
ctrl))
          in [ AnyBezier Double -> Double -> RPoint
forall (b :: * -> *) a.
(GenericBezier b, Unbox a, Fractional a) =>
b a -> a -> V2 a
Bezier.evalBezier AnyBezier Double
bezier (Double -> Double
forall a. Fractional a => a -> a
recip Double
chunksDouble -> Double -> Double
forall a. Num a => a -> a -> a
*Double
i) | Double
i <- [Double
0..Double
chunks]] [RPoint] -> [RPoint] -> [RPoint]
forall a. [a] -> [a] -> [a]
++
          RPoint -> [LineCommand] -> [RPoint]
worker ([RPoint] -> RPoint
forall a. [a] -> a
last [RPoint]
ctrl) [LineCommand]
xs
        LineEnd RPoint
p -> RPoint
p RPoint -> [RPoint] -> [RPoint]
forall a. a -> [a] -> [a]
: RPoint -> [LineCommand] -> [RPoint]
worker RPoint
p [LineCommand]
xs
    chunks :: Double
chunks = Double
10
svgBoundingPoints :: Tree -> [RPoint]
svgBoundingPoints :: Tree -> [RPoint]
svgBoundingPoints Tree
t = (RPoint -> RPoint) -> [RPoint] -> [RPoint]
forall a b. (a -> b) -> [a] -> [b]
map (Matrix Double -> RPoint -> RPoint
Transform.transformPoint Matrix Double
m) ([RPoint] -> [RPoint]) -> [RPoint] -> [RPoint]
forall a b. (a -> b) -> a -> b
$
    case Tree
t of
      Tree
None             -> []
      UseTree{}        -> []
      GroupTree Group
g      -> (Tree -> [RPoint]) -> [Tree] -> [RPoint]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Tree -> [RPoint]
svgBoundingPoints (Group
g Group -> Getting [Tree] Group [Tree] -> [Tree]
forall s a. s -> Getting a s a -> a
^. Getting [Tree] Group [Tree]
Lens' Group [Tree]
groupChildren)
      SymbolTree Group
g     -> (Tree -> [RPoint]) -> [Tree] -> [RPoint]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Tree -> [RPoint]
svgBoundingPoints (Group
g Group -> Getting [Tree] Group [Tree] -> [Tree]
forall s a. s -> Getting a s a -> a
^. Getting [Tree] Group [Tree]
Lens' Group [Tree]
groupChildren)
      FilterTree{}     -> []
      DefinitionTree{} -> []
      PathTree Path
p       -> [LineCommand] -> [RPoint]
linePoints ([LineCommand] -> [RPoint]) -> [LineCommand] -> [RPoint]
forall a b. (a -> b) -> a -> b
$ [PathCommand] -> [LineCommand]
toLineCommands (Path
p Path -> Getting [PathCommand] Path [PathCommand] -> [PathCommand]
forall s a. s -> Getting a s a -> a
^. Getting [PathCommand] Path [PathCommand]
Lens' Path [PathCommand]
pathDefinition)
      CircleTree Circle
c     -> Circle -> [RPoint]
circleBoundingPoints Circle
c
      PolyLineTree PolyLine
pl  -> PolyLine
pl PolyLine -> Getting [RPoint] PolyLine [RPoint] -> [RPoint]
forall s a. s -> Getting a s a -> a
^. Getting [RPoint] PolyLine [RPoint]
Lens' PolyLine [RPoint]
polyLinePoints
      EllipseTree Ellipse
e    -> Ellipse -> [RPoint]
ellipseBoundingPoints Ellipse
e
      LineTree Line
l       -> ((Number, Number) -> RPoint) -> [(Number, Number)] -> [RPoint]
forall a b. (a -> b) -> [a] -> [b]
map (Number, Number) -> RPoint
pointToRPoint [Line
l Line
-> Getting (Number, Number) Line (Number, Number)
-> (Number, Number)
forall s a. s -> Getting a s a -> a
^. Getting (Number, Number) Line (Number, Number)
Lens' Line (Number, Number)
linePoint1, Line
l Line
-> Getting (Number, Number) Line (Number, Number)
-> (Number, Number)
forall s a. s -> Getting a s a -> a
^. Getting (Number, Number) Line (Number, Number)
Lens' Line (Number, Number)
linePoint2]
      RectangleTree Rectangle
r  ->
        let p :: RPoint
p = (Number, Number) -> RPoint
pointToRPoint (Rectangle
r Rectangle
-> Getting (Number, Number) Rectangle (Number, Number)
-> (Number, Number)
forall s a. s -> Getting a s a -> a
^. Getting (Number, Number) Rectangle (Number, Number)
Lens' Rectangle (Number, Number)
rectUpperLeftCorner)
            mDims :: (Maybe Number, Maybe Number)
mDims = (Rectangle
r Rectangle
-> Getting (Maybe Number) Rectangle (Maybe Number) -> Maybe Number
forall s a. s -> Getting a s a -> a
^. Getting (Maybe Number) Rectangle (Maybe Number)
Lens' Rectangle (Maybe Number)
rectWidth, Rectangle
r Rectangle
-> Getting (Maybe Number) Rectangle (Maybe Number) -> Maybe Number
forall s a. s -> Getting a s a -> a
^. Getting (Maybe Number) Rectangle (Maybe Number)
Lens' Rectangle (Maybe Number)
rectHeight)
        in  RPoint -> (Maybe Number, Maybe Number) -> [RPoint]
rectPoints RPoint
p (Maybe Number, Maybe Number)
mDims
      TextTree{}       -> []
      ImageTree Image
img    ->
        let p :: RPoint
p = (Number, Number) -> RPoint
pointToRPoint (Image
img Image
-> Getting (Number, Number) Image (Number, Number)
-> (Number, Number)
forall s a. s -> Getting a s a -> a
^. Getting (Number, Number) Image (Number, Number)
Lens' Image (Number, Number)
imageCornerUpperLeft)
            dims :: (Number, Number)
dims = (Image
img Image -> Getting Number Image Number -> Number
forall s a. s -> Getting a s a -> a
^. Getting Number Image Number
Lens' Image Number
imageWidth, Image
img Image -> Getting Number Image Number -> Number
forall s a. s -> Getting a s a -> a
^. Getting Number Image Number
Lens' Image Number
imageHeight)
        in  RPoint -> (Number, Number) -> [RPoint]
rectPoints' RPoint
p (Number, Number)
dims
      MeshGradientTree{} -> []
      SvgTree Document
d        -> let mDims :: (Maybe Number, Maybe Number)
mDims = (Document
d Document
-> Getting (Maybe Number) Document (Maybe Number) -> Maybe Number
forall s a. s -> Getting a s a -> a
^. Getting (Maybe Number) Document (Maybe Number)
Lens' Document (Maybe Number)
documentWidth, Document
d Document
-> Getting (Maybe Number) Document (Maybe Number) -> Maybe Number
forall s a. s -> Getting a s a -> a
^. Getting (Maybe Number) Document (Maybe Number)
Lens' Document (Maybe Number)
documentHeight)
                          in  RPoint -> (Maybe Number, Maybe Number) -> [RPoint]
rectPoints (Double -> Double -> RPoint
forall a. a -> a -> V2 a
V2 Double
0 Double
0) (Maybe Number, Maybe Number)
mDims
      Tree
_                -> []
  where
    m :: Matrix Double
m = Maybe [Transformation] -> Matrix Double
Transform.mkMatrix (Tree
t Tree
-> Getting (Maybe [Transformation]) Tree (Maybe [Transformation])
-> Maybe [Transformation]
forall s a. s -> Getting a s a -> a
^. Getting (Maybe [Transformation]) Tree (Maybe [Transformation])
forall c. HasDrawAttributes c => Lens' c (Maybe [Transformation])
transform)
    mapTuple :: a b' c' -> a (b', b') (c', c')
mapTuple a b' c'
f = a b' c'
f a b' c' -> a b' c' -> a (b', b') (c', c')
forall (a :: * -> * -> *) b c b' c'.
Arrow a =>
a b c -> a b' c' -> a (b, b') (c, c')
*** a b' c'
f
    toUserUnit' :: Number -> Number
toUserUnit' = Dpi -> Number -> Number
toUserUnit Dpi
defaultDPI
    pointToRPoint :: (Number, Number) -> RPoint
pointToRPoint (Number, Number)
p =
      case (Number -> Number) -> (Number, Number) -> (Number, Number)
forall (a :: * -> * -> *) b' c'.
Arrow a =>
a b' c' -> a (b', b') (c', c')
mapTuple Number -> Number
toUserUnit' (Number, Number)
p of
        (Num Double
x, Num Double
y) -> Double -> Double -> RPoint
forall a. a -> a -> V2 a
V2 Double
x Double
y
        (Number, Number)
_              -> [Char] -> RPoint
forall a. HasCallStack => [Char] -> a
error [Char]
"Reanimate.Svg.svgBoundingPoints: Unrecognized number format."
    circleBoundingPoints :: Circle -> [RPoint]
circleBoundingPoints Circle
circ =
      let (Number
xnum, Number
ynum) = Circle
circ Circle
-> Getting (Number, Number) Circle (Number, Number)
-> (Number, Number)
forall s a. s -> Getting a s a -> a
^. Getting (Number, Number) Circle (Number, Number)
Lens' Circle (Number, Number)
circleCenter
          rnum :: Number
rnum = Circle
circ Circle -> Getting Number Circle Number -> Number
forall s a. s -> Getting a s a -> a
^. Getting Number Circle Number
Lens' Circle Number
circleRadius
      in case (Number -> Maybe Double) -> [Number] -> [Double]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe Number -> Maybe Double
unpackNumber [Number
xnum, Number
ynum, Number
rnum] of
        [Double
x, Double
y, Double
r] -> Double -> Double -> Double -> Double -> [RPoint]
forall a. (Floating a, Enum a) => a -> a -> a -> a -> [V2 a]
ellipsePoints Double
x Double
y Double
r Double
r
        [Double]
_         -> []
    ellipseBoundingPoints :: Ellipse -> [RPoint]
ellipseBoundingPoints Ellipse
e =
      let (Number
xnum,Number
ynum) = Ellipse
e Ellipse
-> Getting (Number, Number) Ellipse (Number, Number)
-> (Number, Number)
forall s a. s -> Getting a s a -> a
^. Getting (Number, Number) Ellipse (Number, Number)
Lens' Ellipse (Number, Number)
ellipseCenter
          xrnum :: Number
xrnum = Ellipse
e Ellipse -> Getting Number Ellipse Number -> Number
forall s a. s -> Getting a s a -> a
^. Getting Number Ellipse Number
Lens' Ellipse Number
ellipseXRadius
          yrnum :: Number
yrnum = Ellipse
e Ellipse -> Getting Number Ellipse Number -> Number
forall s a. s -> Getting a s a -> a
^. Getting Number Ellipse Number
Lens' Ellipse Number
ellipseYRadius
      in case (Number -> Maybe Double) -> [Number] -> [Double]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe Number -> Maybe Double
unpackNumber [Number
xnum, Number
ynum, Number
xrnum, Number
yrnum] of
        [Double
x, Double
y, Double
xr, Double
yr] -> Double -> Double -> Double -> Double -> [RPoint]
forall a. (Floating a, Enum a) => a -> a -> a -> a -> [V2 a]
ellipsePoints Double
x Double
y Double
xr Double
yr
        [Double]
_              -> []
    ellipsePoints :: a -> a -> a -> a -> [V2 a]
ellipsePoints a
x a
y a
xr a
yr = [ a -> a -> V2 a
forall a. a -> a -> V2 a
V2 (a
x a -> a -> a
forall a. Num a => a -> a -> a
+ a
xr a -> a -> a
forall a. Num a => a -> a -> a
* a -> a
forall a. Floating a => a -> a
cos a
angle) (a
y a -> a -> a
forall a. Num a => a -> a -> a
+ a
yr a -> a -> a
forall a. Num a => a -> a -> a
* a -> a
forall a. Floating a => a -> a
sin a
angle)
                              | a
angle <- [a
0, a
forall a. Floating a => a
pia -> a -> a
forall a. Fractional a => a -> a -> a
/a
10 .. a
2 a -> a -> a
forall a. Num a => a -> a -> a
* a
forall a. Floating a => a
pi] ]
    rectPoints :: RPoint -> (Maybe Number, Maybe Number) -> [RPoint]
rectPoints RPoint
p (Maybe Number, Maybe Number)
mDims = case (Maybe Number, Maybe Number)
mDims of
                           (Just Number
w, Just Number
h) -> RPoint -> (Number, Number) -> [RPoint]
rectPoints' RPoint
p (Number
w, Number
h)
                           (Maybe Number, Maybe Number)
_ -> [RPoint
p]
    rectPoints' :: RPoint -> (Number, Number) -> [RPoint]
rectPoints' p :: RPoint
p@(V2 Double
x Double
y) (Number, Number)
dims =
      RPoint
p RPoint -> [RPoint] -> [RPoint]
forall a. a -> [a] -> [a]
: case (Number -> Number) -> (Number, Number) -> (Number, Number)
forall (a :: * -> * -> *) b' c'.
Arrow a =>
a b' c' -> a (b', b') (c', c')
mapTuple Number -> Number
toUserUnit' (Number, Number)
dims of
            ((Num Double
w), (Num Double
h)) -> let (Double
x', Double
y') = (Double
x Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
w, Double
y Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
h)
                                  in  [Double -> Double -> RPoint
forall a. a -> a -> V2 a
V2 Double
x' Double
y, Double -> Double -> RPoint
forall a. a -> a -> V2 a
V2 Double
x' Double
y', Double -> Double -> RPoint
forall a. a -> a -> V2 a
V2 Double
x Double
y']
            (Number, Number)
_ -> []
    unpackNumber :: Number -> Maybe Double
unpackNumber Number
n =
      case Number -> Number
toUserUnit' Number
n of
        Num Double
d -> Double -> Maybe Double
forall a. a -> Maybe a
Just Double
d
        Number
_     -> Maybe Double
forall a. Maybe a
Nothing