module BarChart where import Color exposing (..) import Graphics.Collage exposing (..) import Graphics.Element exposing (..) import Time exposing (..) import Keyboard import Signal import List exposing (..) import Array exposing (Array) import Text exposing (..) import Window exposing (..) import Html exposing (Html,div) import Html.Events exposing (on,onMouseUp) import Html.Attributes as Attributes import Task exposing (Task) import Effects exposing (Never) import StartApp import Json.Decode as Decode ----------------------------------------------------------------- --a sample title slide; yours should be way more interesting slide9 : Float -> List Form slide9 t = let title = text (Text.fromString "My Sample Presentation") |> scale 2 |> move (0, 100) animation t = group [filled red (ngon 3 50) |> rotate (degrees (5*t)) ] in [title,animation t] -- this is a sample slide for a pie chart slide10 : Float -> List Form slide10 t = let title = text (Text.fromString "Title for This Graph") |> move (0,200) |> scale 2 rawData : List (String, number, Color) rawData = [("2010",120,yellow),("2011",200,blue) ,("2012",80,red),("2013",120,green) ,("2014",200,black),("2015",80,purple)] radius = 100 mkTri n color = (polygon [(0,0),(radius,0), (radius*cos(degrees 1),radius*sin(degrees 1))]) |> filled color |> rotate (degrees (toFloat n)) circle = List.map2 mkTri [0..360] mkSlices : List (String, Int, Color) -> Form mkSlices rawData = let degreeRatio = 360 / (toFloat (List.sum (List.map dataPoint rawData))) mkSlices' : List (String, Int, Color) -> Int -> List Form mkSlices' rawData deg = case rawData of [] -> [] (l,n,c)::xs -> List.map2 mkTri [deg..(deg + (round ((toFloat n)*degreeRatio)))] (List.repeat (round ((toFloat n)*degreeRatio)) c) ++ mkSlices' xs (deg + (round ((toFloat n)*degreeRatio))) allSlices = group (mkSlices' rawData 0) in allSlices mkLegend : List (String, Int, Color) -> Form mkLegend rawData = let numPoints = List.length rawData mkLegend' : List (String, Int, Color) -> Int -> List Form mkLegend' rawData index = case rawData of [] -> [] (l,n,c)::xs -> [ text (Text.fromString l) |> move (0,-(toFloat index)*40+(toFloat numPoints)*20) , rect 30 30 |> filled c |> move (-100,-(toFloat index)*40+(toFloat numPoints)*20) ] ++ mkLegend' xs (index + 1) in group (mkLegend' rawData 0) |> move (-300,0) label (l,_,_) = l dataPoint (_,n,_) = n colour (_,_,c) = c in [mkSlices rawData, mkLegend rawData,title] --this is an example slide for a bar chart slide11 : Float -> List Form slide11 t = let growth = grow t 5 -- growth goes from 0 to 1 in 5 seconds --fill in the data for a bar chart here rawData : List (String, number, Color) rawData = [("2010",120,yellow),("2011",200,green),("2012",80,yellow),("2013",120,green),("2014",200,yellow),("2015",80,green)] label (l,_,_) = l number (_,n,_) = n colour (_,_,c) = c yLabel = "Population" xLabel = "Year" numBars = List.length rawData maxim = List.maximum (map number rawData) rectangles = map2 mkRect rawData [0..numBars] mkRect : (String,Float,Color) -> Int -> Form mkRect (str,n,clr) xPos = case maxim of Nothing -> rect 5 5 --this case only happens with no data |> filled clr --just make a rectangle |> move (-100,0) Just m -> rect (width/(toFloat numBars)-10) (growth*height*n/m) |> filled clr |> move ((toFloat xPos)*(width/(toFloat numBars))-width/3,-140+growth*height/2*n/m ) xLabels = map2 mkXText rawData [0..numBars] mkXText : (String,Float,Color) -> Int -> Form mkXText (str,n,clr) xPos = text (style (captionStyle) (fromString str)) |> move ((toFloat xPos)*(width/(toFloat numBars))-width/3,-height/2) yNums = case maxim of Nothing -> [] Just m -> map ((*)(m//10)) [0..10] yLabels = map2 mkYText yNums [0..10] mkYText num pos = text (style (captionStyle) (fromString(toString num))) |> move (-width/2-30,pos*height/10-140) yText = text (style (captionStyle) (fromString yLabel)) |> move (-width/2-100,height/4) |> rotate (degrees 90) xText = text (style (captionStyle) (fromString xLabel)) |> move (width/4, -height/2-50) width = 600 --total graph width and height height = 400 in xText::yText::rectangles ++ xLabels ++ yLabels -- FUNCTIONS ------------------- mod x n = let y = toFloat (floor (x / n)) -- This function is how I make things loop! in x - y * n grow t tLoop = (1 / tLoop) * (mod t tLoop) trans t y = if t < 0 -- Used for all permanent transitions (fading out, color change, etc.) then 0 else if (t/500) > (pi/2) then y else sin(t/500) * y drawLine t (x1,y1) (x2,y2) = let distanceX = x2-x1 distanceY = y2-y1 newX = x1 + (trans t distanceX) newY = y1 + (trans t distanceY) in if (x1,y1) == (newX,newY) then segment (4000,4000) (4000,4000) else segment (x1,y1) (newX,newY) -------------------------------- -- STYLES ------------------- borderBlue = { color = blue , width = 3.0 , cap = Round , join = Smooth , dashing = [] , dashOffset = 0 } thickRed = { color = red , width = 10.0 , cap = Round , join = Smooth , dashing = [] , dashOffset = 0 } thickGreen = { color = green , width = 10.0 , cap = Round , join = Smooth , dashing = [] , dashOffset = 0 } titleStyle4 t = { typeface = ["hi"] , height = Just 35 , color = rgb 0 0 0 , bold = True , italic = False , line = Nothing } captionStyle = { typeface = ["hi"] , height = Just 30.0 , color = rgb 100 100 100 , bold = True , italic = False , line = Nothing } orangeOutline = { color = orange , width = 1.5 , cap = Round , join = Smooth , dashing = [] , dashOffset = 0 } -------------------------------- -- Code used to create the slides (Not mine credit goes to other group.) type alias Model = (Float,Int) type Events = Arrows {x : Int, y:Int} | Jump Int -- the slide to jump to | TimeIncrease Float events : Signal Events events = Signal.merge (Signal.map Arrows Keyboard.arrows) (Signal.map TimeIncrease (fps 30)) app : { html : Signal Html , model : Signal Model , tasks : Signal (Task Never ()) } app = StartApp.start { init = ( (0,-1) -- t=0, no slide selected , Effects.tick TimeIncrease) , update = update , view = view , inputs = [ Signal.map Arrows Keyboard.arrows , Signal.map TimeIncrease (fps 30) ] } main : Signal Html main = app.html port tasks : Signal (Task Never ()) port tasks = app.tasks marginWidth = 74 width = 1024 - 74 height = 758 thumbWidth = 72 thumbHeight = 58 update event (t,idx) = (case event of Arrows arrows -> (0 , moveIdx arrows.x idx) Jump to -> (0 , to) TimeIncrease dt -> (t + 0.001 * dt, idx) ,Effects.none ) moveIdx move oldIdx = case move of 1 -> if oldIdx + 1 < Array.length slides then oldIdx + 1 else oldIdx (-1) -> if oldIdx > 0 then oldIdx - 1 else oldIdx otherwise -> oldIdx onTouchStart : Signal.Address a -> a -> Html.Attribute onTouchStart address action = on "touchstart" Decode.value (\_ -> Signal.message address action) onTouchEnd : Signal.Address a -> a -> Html.Attribute onTouchEnd address action = on "touchend" Decode.value (\_ -> Signal.message address action) view : Signal.Address Events -> Model -> Html view address (t,idx) = div [] [ div [ Attributes.style [("height", "768px") ,("margin", "auto") ,("position", "relative") ,("width", "1024px") ] ] ( (map (\ (sideIdx,slide)-> div [ Attributes.style [("height",toString (thumbHeight+2) ++ "px") ,("width",toString marginWidth ++ "px")] , onTouchEnd address (Jump sideIdx) , onMouseUp address (Jump sideIdx) ] [ Html.fromElement <| collage thumbWidth thumbHeight [scale 0.024 <| group <| slide 0]] ) (Array.toIndexedList slides) ) ++ [ div [Attributes.style [("height",toString height ++ "px") ,("width",toString width ++"px") ,("position","absolute") ,("left",toString marginWidth ++ "px") ,("top","0px") ]] -- [Html.text <| toString <| (t,idx)] -- debug, just time and slide number [Html.fromElement <| collage width height (case Array.get idx slides of Just slide -> slide t Nothing -> [] ) ] ] ) ] -- slides : Array (Float -> List Form) slides = Array.fromList [slide9, slide10, slide11] numSlides = Array.length slides