1- module Visualization.Shape exposing (line , linearCurve , monotoneInXCurve , Curve )
1+ module Visualization.Shape exposing (line , area , linearCurve , monotoneInXCurve , Curve )
22
33{- | Visualizations typically consist of discrete graphical marks, such as symbols,
44arcs, lines and areas. While the rectangles of a bar chart may be easy enough to
@@ -10,7 +10,7 @@ variety of shape generators for your convenience.
1010
1111# Lines
1212
13- @docs line
13+ @docs line, area
1414
1515# Curves
1616
@@ -30,10 +30,24 @@ into drawing commands.
3030-}
3131type Curve
3232 = Line ( List Point )
33+ | Area ( List ( Point , Point ))
3334
3435
36+ applyRecursivelyForArea : (Curve -> List PathSegment ) -> List ( Point , Point ) -> List PathSegment
37+ applyRecursivelyForArea fn points =
38+ let
39+ points0 =
40+ List . map fst points
41+
42+ points1 =
43+ List . reverse <| List . map snd points
44+ in
45+ case ( fn ( Line points1) , points1 ) of
46+ ( _ :: tail, ( x, y ) :: _ ) ->
47+ ( fn ( Line points0) |> Path . lineTo x y) ++ ( tail |> Path . close)
3548
36- -- | Area (List Point) (List Point)
49+ _ ->
50+ []
3751
3852
3953{- | Produces a polyline through the specified points.
@@ -47,6 +61,9 @@ linearCurve part =
4761 Line ( point :: points) ->
4862 Path . Move point :: List . map Path . Line points
4963
64+ Area points ->
65+ applyRecursivelyForArea linearCurve points
66+
5067
5168{- | Produces a cubic spline that [preserves monotonicity](http://adsabs.harvard.edu/full/1990A%26A...239..443S)
5269in y, assuming monotonicity in x, as proposed by Steffen in
@@ -98,6 +115,9 @@ monotoneInXCurve part =
98115 Line ( point0 :: point1 :: points) ->
99116 finalize <| List . foldl helper ( point0, point1, Nothing , [ Path . Move point0 ] ) points
100117
118+ Area points ->
119+ applyRecursivelyForArea monotoneInXCurve points
120+
101121
102122sign : Float -> Float
103123sign x =
@@ -163,6 +183,20 @@ Points accepted are `Maybe`s, Nothing represent gaps in the data and correspondi
163183gaps will be rendered in the line.
164184
165185**Note:** A single point (surrounded by Nothing) may not be visible.
186+
187+ Usually you will need to convert your data into a format supported by this function.
188+ For example, if your data is a `List (Date, Float)`, you might use something like:
189+
190+ lineGenerator : ( Date, Float ) -> Maybe ( Float, Float )
191+ lineGenerator ( x, y ) =
192+ Just ( Scale.convert xScale x, Scale.convert yScale y )
193+
194+ linePath : List (Date, Float) -> String
195+ linePath data =
196+ List.map lineGenerator data
197+ |> Shape.line Shape.linearCurve
198+
199+ where `xScale` and `yScale` would be appropriate `Scale`s.
166200-}
167201line : (Curve -> List PathSegment ) -> List (Maybe Point ) -> String
168202line curve data =
@@ -182,3 +216,50 @@ line curve data =
182216 ( Just p1, Line [ p1 ] :: l )
183217 in
184218 toAttrString <| List . concatMap curve <| snd <| List . foldr makeCurves ( Nothing , [] ) data
219+
220+
221+ {- | The area generator produces an area, as in an area chart. An area is defined
222+ by two bounding lines, either splines or polylines. Typically, the two lines
223+ share the same x-values (x0 = x1), differing only in y-value (y0 and y1);
224+ most commonly, y0 is defined as a constant representing zero. The first line
225+ (the topline) is defined by x1 and y1 and is rendered first; the second line
226+ (the baseline) is defined by x0 and y0 and is rendered second, with the points
227+ in reverse order. With a `linearCurve` curve, this produces a clockwise polygon.
228+
229+ The data attribute you pass in should be a `[Just ((x0, y0), (x1, y1))]`. Passing
230+ in `Nothing` represents gaps in the data and corresponding gaps in the area will
231+ be rendered.
232+
233+ Usually you will need to convert your data into a format supported by this function.
234+ For example, if your data is a `List (Date, Float)`, you might use something like:
235+
236+ areaGenerator : ( Date, Float ) -> Maybe ( ( Float, Float ), ( Float, Float ) )
237+ areaGenerator ( x, y ) =
238+ Just ( ( Scale.convert xScale x, fst (Scale.rangeExtent yScale) ),
239+ ( Scale.convert xScale x, Scale.convert yScale y ) )
240+
241+ areaPath : List (Date, Float) -> String
242+ areaPath data =
243+ List.map areaGenerator data
244+ |> Shape.area Shape.linearCurve
245+
246+ where `xScale` and `yScale` would be appropriate `Scale`s.
247+ -}
248+ area : (Curve -> List PathSegment ) -> List (Maybe ( Point , Point )) -> String
249+ area curve data =
250+ let
251+ makeCurves datum ( prev, list ) =
252+ case ( prev, datum, list ) of
253+ ( _, Nothing , l ) ->
254+ ( Nothing , l )
255+
256+ ( Nothing , Just pair, l ) ->
257+ ( Just pair, Area [ pair ] :: l )
258+
259+ ( Just p0, Just p1, ( Area ps) :: l ) ->
260+ ( Just p1, Area ( p1 :: ps) :: l )
261+
262+ ( Just p0, Just p1, l ) ->
263+ ( Just p1, Area [ p1 ] :: l )
264+ in
265+ toAttrString <| List . concatMap curve <| snd <| List . foldr makeCurves ( Nothing , [] ) data
0 commit comments