never executed always true always false
    1 module Reanimate.Svg.BoundingBox where
    2 
    3 import           Control.Arrow             ((***))
    4 import           Control.Lens              ((^.))
    5 import           Data.List
    6 import           Data.Maybe                (mapMaybe)
    7 import           Graphics.SvgTree          hiding (height, line, path, use,
    8                                             width)
    9 import           Linear.V2                 hiding (angle)
   10 import           Linear.Vector
   11 import           Reanimate.Constants
   12 import           Reanimate.Svg.LineCommand
   13 import qualified Reanimate.Transform       as Transform
   14 -- import qualified Geom2D.CubicBezier           as Bezier
   15 
   16 -- | Return bounding box of SVG tree.
   17 --  The four numbers returned are (minimal X-coordinate, minimal Y-coordinate, width, height)
   18 --
   19 --  Note: Bounding boxes are computed on a best-effort basis and will not work
   20 --        in all cases. The only supported SVG nodes are: path, circle, polyline,
   21 --        ellipse, line, rectangle, image. All other nodes return (0,0,0,0).
   22 boundingBox :: Tree -> (Double, Double, Double, Double)
   23 boundingBox t =
   24     case svgBoundingPoints t of
   25       [] -> (0,0,0,0)
   26       (V2 x y:rest) ->
   27         let (minx, miny, maxx, maxy) = foldl' worker (x, y, x, y) rest
   28         in (minx, miny, maxx-minx, maxy-miny)
   29   where
   30     worker (minx, miny, maxx, maxy) (V2 x y) =
   31       (min minx x, min miny y, max maxx x, max maxy y)
   32 
   33 svgHeight :: Tree -> Double
   34 svgHeight t = h
   35   where
   36     (_x, _y, _w, h) = boundingBox t
   37 
   38 svgWidth :: Tree -> Double
   39 svgWidth t = w
   40   where
   41     (_x, _y, w, _h) = boundingBox t
   42 
   43 linePoints :: [LineCommand] -> [RPoint]
   44 linePoints = worker zero
   45   where
   46     worker _from [] = []
   47     worker from (x:xs) =
   48       case x of
   49         LineMove to     -> worker to xs
   50         -- LineDraw to     -> from:to:worker to xs
   51         LineBezier [p] ->
   52           p : worker p xs
   53         LineBezier ctrl -> -- approximation
   54           [ last (partialBezierPoints (from:ctrl) 0 (recip chunks*i)) | i <- [0..chunks]] ++
   55           worker (last ctrl) xs
   56         LineEnd p -> p : worker p xs
   57     chunks = 10
   58 
   59 svgBoundingPoints :: Tree -> [RPoint]
   60 svgBoundingPoints t = map (Transform.transformPoint m) $
   61     case t of
   62       None            -> []
   63       UseTree{}       -> []
   64       GroupTree g     -> concatMap svgBoundingPoints (g^.groupChildren)
   65       SymbolTree (Symbol g) -> concatMap svgBoundingPoints (g^.groupChildren)
   66       FilterTree{}    -> []
   67       DefinitionTree{} -> []
   68       PathTree p      -> linePoints $ toLineCommands (p^.pathDefinition)
   69       CircleTree c    -> circleBoundingPoints c
   70       PolyLineTree pl -> pl ^. polyLinePoints
   71       EllipseTree e   -> ellipseBoundingPoints e
   72       LineTree line   -> map pointToRPoint [line^.linePoint1, line^.linePoint2]
   73       RectangleTree rect ->
   74         case pointToRPoint (rect^.rectUpperLeftCorner) of
   75           V2 x y -> V2 x y :
   76             case mapTuple (fmap $ toUserUnit defaultDPI) (rect^.rectWidth, rect^.rectHeight) of
   77               (Just (Num w), Just (Num h)) -> [V2 (x+w) (y+h)]
   78               _                            -> []
   79       TextTree{}      -> []
   80       ImageTree img   ->
   81         case (img^.imageCornerUpperLeft, img^.imageWidth, img^.imageHeight) of
   82           ((Num x, Num y), Num w, Num h) ->
   83             [V2 x y, V2 (x+w) (y+h)]
   84           _ -> []
   85       MeshGradientTree{} -> []
   86       _ -> []
   87   where
   88     m = Transform.mkMatrix (t^.transform)
   89     mapTuple f = f *** f
   90     pointToRPoint p =
   91       case mapTuple (toUserUnit defaultDPI) p of
   92         (Num x, Num y) -> V2 x y
   93         _ -> error "Reanimate.Svg.svgBoundingPoints: Unrecognized number format."
   94 
   95     circleBoundingPoints circ =
   96       let (xnum, ynum) = circ ^. circleCenter
   97           rnum = circ ^. circleRadius
   98       in case mapMaybe unpackNumber [xnum, ynum, rnum] of
   99         [x, y, r] -> [ V2 (x + r * cos angle) (y + r * sin angle) | angle <- [0, pi/10 .. 2 * pi]]
  100         _  -> []
  101 
  102     ellipseBoundingPoints e =
  103       let (xnum,ynum) = e ^. ellipseCenter
  104           xrnum = e ^. ellipseXRadius
  105           yrnum = e ^. ellipseYRadius
  106       in case mapMaybe unpackNumber [xnum, ynum, xrnum, yrnum] of
  107         [x,y,xr,yr] -> [V2 (x + xr * cos angle) (y + yr * sin angle) | angle <- [0, pi/10 .. 2 * pi]]
  108         _ -> []
  109 
  110     unpackNumber n =
  111       case toUserUnit defaultDPI n of
  112         Num d -> Just d
  113         _     -> Nothing