reanimate/reanimate-0.4.1.0-inplace/Reanimate.Svg.BoundingBox.hs.html
2020-08-27 05:53:54 +00:00

153 lines
16 KiB
HTML

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style type="text/css">
span.lineno { color: white; background: #aaaaaa; border-right: solid white 12px }
span.nottickedoff { background: yellow}
span.istickedoff { background: white }
span.tickonlyfalse { margin: -1px; border: 1px solid #f20913; background: #f20913 }
span.tickonlytrue { margin: -1px; border: 1px solid #60de51; background: #60de51 }
span.funcount { font-size: small; color: orange; z-index: 2; position: absolute; right: 20 }
span.decl { font-weight: bold }
span.spaces { background: white }
</style>
</head>
<body>
<pre>
<span class="decl"><span class="nottickedoff">never executed</span> <span class="tickonlytrue">always true</span> <span class="tickonlyfalse">always false</span></span>
</pre>
<pre>
<span class="lineno"> 1 </span>{-|
<span class="lineno"> 2 </span> Bounding-boxes can be immensely useful for aligning objects
<span class="lineno"> 3 </span> but they are not part of the SVG specification and cannot be
<span class="lineno"> 4 </span> computed for all SVG nodes. In particular, you'll get bad results
<span class="lineno"> 5 </span> when asking for the bounding boxes of Text nodes (because fonts
<span class="lineno"> 6 </span> are difficult), clipped nodes, and filtered nodes.
<span class="lineno"> 7 </span>-}
<span class="lineno"> 8 </span>module Reanimate.Svg.BoundingBox
<span class="lineno"> 9 </span> ( boundingBox
<span class="lineno"> 10 </span> , svgHeight
<span class="lineno"> 11 </span> , svgWidth
<span class="lineno"> 12 </span> ) where
<span class="lineno"> 13 </span>
<span class="lineno"> 14 </span>import Control.Arrow ((***))
<span class="lineno"> 15 </span>import Control.Lens ((^.))
<span class="lineno"> 16 </span>import Data.List
<span class="lineno"> 17 </span>import Data.Maybe (mapMaybe)
<span class="lineno"> 18 </span>import qualified Data.Vector.Unboxed as V
<span class="lineno"> 19 </span>import qualified Geom2D.CubicBezier.Linear as Bezier
<span class="lineno"> 20 </span>import Graphics.SvgTree hiding (height, line, path, use, width)
<span class="lineno"> 21 </span>import Linear.V2 hiding (angle)
<span class="lineno"> 22 </span>import Linear.Vector
<span class="lineno"> 23 </span>import Reanimate.Constants
<span class="lineno"> 24 </span>import Reanimate.Svg.LineCommand
<span class="lineno"> 25 </span>import qualified Reanimate.Transform as Transform
<span class="lineno"> 26 </span>
<span class="lineno"> 27 </span>-- | Return bounding box of SVG tree.
<span class="lineno"> 28 </span>-- The four numbers returned are (minimal X-coordinate, minimal Y-coordinate, width, height)
<span class="lineno"> 29 </span>--
<span class="lineno"> 30 </span>-- Note: Bounding boxes are computed on a best-effort basis and will not work
<span class="lineno"> 31 </span>-- in all cases. The only supported SVG nodes are: path, circle, polyline,
<span class="lineno"> 32 </span>-- ellipse, line, rectangle, image. All other nodes return (0,0,0,0).
<span class="lineno"> 33 </span>boundingBox :: Tree -&gt; (Double, Double, Double, Double)
<span class="lineno"> 34 </span><span class="decl"><span class="istickedoff">boundingBox t =</span>
<span class="lineno"> 35 </span><span class="spaces"> </span><span class="istickedoff">case svgBoundingPoints t of</span>
<span class="lineno"> 36 </span><span class="spaces"> </span><span class="istickedoff">[] -&gt; (0,0,0,0)</span>
<span class="lineno"> 37 </span><span class="spaces"> </span><span class="istickedoff">(V2 x y:rest) -&gt;</span>
<span class="lineno"> 38 </span><span class="spaces"> </span><span class="istickedoff">let (minx, miny, maxx, maxy) = foldl' worker (x, y, x, y) rest</span>
<span class="lineno"> 39 </span><span class="spaces"> </span><span class="istickedoff">in (minx, miny, maxx-minx, maxy-miny)</span>
<span class="lineno"> 40 </span><span class="spaces"> </span><span class="istickedoff">where</span>
<span class="lineno"> 41 </span><span class="spaces"> </span><span class="istickedoff">worker (minx, miny, maxx, maxy) (V2 x y) =</span>
<span class="lineno"> 42 </span><span class="spaces"> </span><span class="istickedoff">(min minx x, min miny y, max maxx x, max maxy y)</span></span>
<span class="lineno"> 43 </span>
<span class="lineno"> 44 </span>-- | Height of SVG node in local units (not pixels). Computed on best-effort basis
<span class="lineno"> 45 </span>-- and will not give accurate results for all SVG nodes.
<span class="lineno"> 46 </span>svgHeight :: Tree -&gt; Double
<span class="lineno"> 47 </span><span class="decl"><span class="nottickedoff">svgHeight t = h</span>
<span class="lineno"> 48 </span><span class="spaces"> </span><span class="nottickedoff">where</span>
<span class="lineno"> 49 </span><span class="spaces"> </span><span class="nottickedoff">(_x, _y, _w, h) = boundingBox t</span></span>
<span class="lineno"> 50 </span>
<span class="lineno"> 51 </span>-- | Width of SVG node in local units (not pixels). Computed on best-effort basis
<span class="lineno"> 52 </span>-- and will not give accurate results for all SVG nodes.
<span class="lineno"> 53 </span>svgWidth :: Tree -&gt; Double
<span class="lineno"> 54 </span><span class="decl"><span class="nottickedoff">svgWidth t = w</span>
<span class="lineno"> 55 </span><span class="spaces"> </span><span class="nottickedoff">where</span>
<span class="lineno"> 56 </span><span class="spaces"> </span><span class="nottickedoff">(_x, _y, w, _h) = boundingBox t</span></span>
<span class="lineno"> 57 </span>
<span class="lineno"> 58 </span>-- | Sampling of points in a line path.
<span class="lineno"> 59 </span>linePoints :: [LineCommand] -&gt; [RPoint]
<span class="lineno"> 60 </span><span class="decl"><span class="nottickedoff">linePoints = worker zero</span>
<span class="lineno"> 61 </span><span class="spaces"> </span><span class="nottickedoff">where</span>
<span class="lineno"> 62 </span><span class="spaces"> </span><span class="nottickedoff">worker _from [] = []</span>
<span class="lineno"> 63 </span><span class="spaces"> </span><span class="nottickedoff">worker from (x:xs) =</span>
<span class="lineno"> 64 </span><span class="spaces"> </span><span class="nottickedoff">case x of</span>
<span class="lineno"> 65 </span><span class="spaces"> </span><span class="nottickedoff">LineMove to -&gt; worker to xs</span>
<span class="lineno"> 66 </span><span class="spaces"> </span><span class="nottickedoff">-- LineDraw to -&gt; from:to:worker to xs</span>
<span class="lineno"> 67 </span><span class="spaces"> </span><span class="nottickedoff">LineBezier [p] -&gt;</span>
<span class="lineno"> 68 </span><span class="spaces"> </span><span class="nottickedoff">p : worker p xs</span>
<span class="lineno"> 69 </span><span class="spaces"> </span><span class="nottickedoff">LineBezier ctrl -&gt; -- approximation</span>
<span class="lineno"> 70 </span><span class="spaces"> </span><span class="nottickedoff">let bezier = Bezier.AnyBezier (V.fromList (from:ctrl))</span>
<span class="lineno"> 71 </span><span class="spaces"> </span><span class="nottickedoff">in [ Bezier.evalBezier bezier (recip chunks*i) | i &lt;- [0..chunks]] ++</span>
<span class="lineno"> 72 </span><span class="spaces"> </span><span class="nottickedoff">worker (last ctrl) xs</span>
<span class="lineno"> 73 </span><span class="spaces"> </span><span class="nottickedoff">LineEnd p -&gt; p : worker p xs</span>
<span class="lineno"> 74 </span><span class="spaces"> </span><span class="nottickedoff">chunks = 10</span></span>
<span class="lineno"> 75 </span>
<span class="lineno"> 76 </span>svgBoundingPoints :: Tree -&gt; [RPoint]
<span class="lineno"> 77 </span><span class="decl"><span class="istickedoff">svgBoundingPoints t = map (Transform.transformPoint m) $</span>
<span class="lineno"> 78 </span><span class="spaces"> </span><span class="istickedoff">case t of</span>
<span class="lineno"> 79 </span><span class="spaces"> </span><span class="istickedoff">None -&gt; <span class="nottickedoff">[]</span></span>
<span class="lineno"> 80 </span><span class="spaces"> </span><span class="istickedoff">UseTree{} -&gt; <span class="nottickedoff">[]</span></span>
<span class="lineno"> 81 </span><span class="spaces"> </span><span class="istickedoff">GroupTree g -&gt; concatMap svgBoundingPoints (g^.groupChildren)</span>
<span class="lineno"> 82 </span><span class="spaces"> </span><span class="istickedoff">SymbolTree (Symbol g) -&gt; <span class="nottickedoff">concatMap svgBoundingPoints (g^.groupChildren)</span></span>
<span class="lineno"> 83 </span><span class="spaces"> </span><span class="istickedoff">FilterTree{} -&gt; <span class="nottickedoff">[]</span></span>
<span class="lineno"> 84 </span><span class="spaces"> </span><span class="istickedoff">DefinitionTree{} -&gt; <span class="nottickedoff">[]</span></span>
<span class="lineno"> 85 </span><span class="spaces"> </span><span class="istickedoff">PathTree p -&gt; <span class="nottickedoff">linePoints $ toLineCommands (p^.pathDefinition)</span></span>
<span class="lineno"> 86 </span><span class="spaces"> </span><span class="istickedoff">CircleTree c -&gt; <span class="nottickedoff">circleBoundingPoints c</span></span>
<span class="lineno"> 87 </span><span class="spaces"> </span><span class="istickedoff">PolyLineTree pl -&gt; <span class="nottickedoff">pl ^. polyLinePoints</span></span>
<span class="lineno"> 88 </span><span class="spaces"> </span><span class="istickedoff">EllipseTree e -&gt; <span class="nottickedoff">ellipseBoundingPoints e</span></span>
<span class="lineno"> 89 </span><span class="spaces"> </span><span class="istickedoff">LineTree line -&gt; <span class="nottickedoff">map pointToRPoint [line^.linePoint1, line^.linePoint2]</span></span>
<span class="lineno"> 90 </span><span class="spaces"> </span><span class="istickedoff">RectangleTree rect -&gt;</span>
<span class="lineno"> 91 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">case pointToRPoint (rect^.rectUpperLeftCorner) of</span></span>
<span class="lineno"> 92 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">V2 x y -&gt; V2 x y :</span></span>
<span class="lineno"> 93 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">case mapTuple (fmap $ toUserUnit defaultDPI) (rect^.rectWidth, rect^.rectHeight) of</span></span>
<span class="lineno"> 94 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">(Just (Num w), Just (Num h)) -&gt; [V2 (x+w) (y+h)]</span></span>
<span class="lineno"> 95 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">_ -&gt; []</span></span>
<span class="lineno"> 96 </span><span class="spaces"> </span><span class="istickedoff">TextTree{} -&gt; []</span>
<span class="lineno"> 97 </span><span class="spaces"> </span><span class="istickedoff">ImageTree img -&gt;</span>
<span class="lineno"> 98 </span><span class="spaces"> </span><span class="istickedoff">case (img^.imageCornerUpperLeft, img^.imageWidth, img^.imageHeight) of</span>
<span class="lineno"> 99 </span><span class="spaces"> </span><span class="istickedoff">((Num x, Num y), Num w, Num h) -&gt;</span>
<span class="lineno"> 100 </span><span class="spaces"> </span><span class="istickedoff">[V2 x y, V2 (x+w) (y+h)]</span>
<span class="lineno"> 101 </span><span class="spaces"> </span><span class="istickedoff">_ -&gt; <span class="nottickedoff">[]</span></span>
<span class="lineno"> 102 </span><span class="spaces"> </span><span class="istickedoff">MeshGradientTree{} -&gt; <span class="nottickedoff">[]</span></span>
<span class="lineno"> 103 </span><span class="spaces"> </span><span class="istickedoff">_ -&gt; <span class="nottickedoff">[]</span></span>
<span class="lineno"> 104 </span><span class="spaces"> </span><span class="istickedoff">where</span>
<span class="lineno"> 105 </span><span class="spaces"> </span><span class="istickedoff">m = Transform.mkMatrix (t^.transform)</span>
<span class="lineno"> 106 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">mapTuple f = f *** f</span></span>
<span class="lineno"> 107 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">pointToRPoint p =</span></span>
<span class="lineno"> 108 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">case mapTuple (toUserUnit defaultDPI) p of</span></span>
<span class="lineno"> 109 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">(Num x, Num y) -&gt; V2 x y</span></span>
<span class="lineno"> 110 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">_ -&gt; error &quot;Reanimate.Svg.svgBoundingPoints: Unrecognized number format.&quot;</span></span>
<span class="lineno"> 111 </span><span class="spaces"></span><span class="istickedoff"></span>
<span class="lineno"> 112 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">circleBoundingPoints circ =</span></span>
<span class="lineno"> 113 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">let (xnum, ynum) = circ ^. circleCenter</span></span>
<span class="lineno"> 114 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">rnum = circ ^. circleRadius</span></span>
<span class="lineno"> 115 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">in case mapMaybe unpackNumber [xnum, ynum, rnum] of</span></span>
<span class="lineno"> 116 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">[x, y, r] -&gt; [ V2 (x + r * cos angle) (y + r * sin angle) | angle &lt;- [0, pi/10 .. 2 * pi]]</span></span>
<span class="lineno"> 117 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">_ -&gt; []</span></span>
<span class="lineno"> 118 </span><span class="spaces"></span><span class="istickedoff"></span>
<span class="lineno"> 119 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">ellipseBoundingPoints e =</span></span>
<span class="lineno"> 120 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">let (xnum,ynum) = e ^. ellipseCenter</span></span>
<span class="lineno"> 121 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">xrnum = e ^. ellipseXRadius</span></span>
<span class="lineno"> 122 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">yrnum = e ^. ellipseYRadius</span></span>
<span class="lineno"> 123 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">in case mapMaybe unpackNumber [xnum, ynum, xrnum, yrnum] of</span></span>
<span class="lineno"> 124 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">[x,y,xr,yr] -&gt; [V2 (x + xr * cos angle) (y + yr * sin angle) | angle &lt;- [0, pi/10 .. 2 * pi]]</span></span>
<span class="lineno"> 125 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">_ -&gt; []</span></span>
<span class="lineno"> 126 </span><span class="spaces"></span><span class="istickedoff"></span>
<span class="lineno"> 127 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">unpackNumber n =</span></span>
<span class="lineno"> 128 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">case toUserUnit defaultDPI n of</span></span>
<span class="lineno"> 129 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">Num d -&gt; Just d</span></span>
<span class="lineno"> 130 </span><span class="spaces"> </span><span class="istickedoff"><span class="nottickedoff">_ -&gt; Nothing</span></span></span>
</pre>
</body>
</html>