diff --git a/.gitignore b/.gitignore index f4cb512..6677a8f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ elm-stuff/ *.html +node_modules +package-lock.json diff --git a/examples/Axis.elm b/examples/Axis.elm index b35ec6a..a1eecbd 100644 --- a/examples/Axis.elm +++ b/examples/Axis.elm @@ -9,38 +9,44 @@ import Html exposing (Html) main : Html msg main = - let - up = - triangle 10 - |> filled (uniform black) - right = - triangle 10 - |> filled (uniform black) - |> rotate (degrees -90) - xAxis = - segment ( -100, 0 ) ( 100, 0 ) - |> traced (solid thin (uniform black)) - yAxis = - segment ( 0, -100 ) ( 0, 100 ) - |> traced (solid thin (uniform black)) - ex = - fromString "x" - |> rendered - wy = - fromString "y" - |> rendered - box = - square 220 - |> outlined (dot thin (uniform black)) - in - group - [ xAxis - , right |> shift ( 100, 0 ) - , ex |> shift ( 100, -10 ) - , yAxis - , up |> shift ( 0, 100 ) - , wy |> shift ( -10, 100 ) - , box - , ellipse 20 40 |> filled (uniform red) |> shift ( 50, 50 ) - ] - |> svg + let + up = + triangle 10 + |> filled (uniform black) + + right = + triangle 10 + |> filled (uniform black) + |> rotate (degrees -90) + + xAxis = + segment ( -100, 0 ) ( 100, 0 ) + |> traced (solid thin (uniform black)) + + yAxis = + segment ( 0, -100 ) ( 0, 100 ) + |> traced (solid thin (uniform black)) + + ex = + fromString "x" + |> rendered + + wy = + fromString "y" + |> rendered + + box = + square 220 + |> outlined (dot thin (uniform black)) + in + group + [ xAxis + , right |> shift ( 100, 0 ) + , ex |> shift ( 100, -10 ) + , yAxis + , up |> shift ( 0, 100 ) + , wy |> shift ( -10, 100 ) + , box + , ellipse 20 40 |> filled (uniform red) |> shift ( 50, 50 ) + ] + |> svg diff --git a/examples/Composition.elm b/examples/Composition.elm index 5f06456..c427bd7 100644 --- a/examples/Composition.elm +++ b/examples/Composition.elm @@ -15,11 +15,12 @@ import Html exposing (Html) type alias Model = - { active : Bool } + { active : Bool } init : Model -init = { active = False } +init = + { active = False } @@ -27,13 +28,14 @@ init = { active = False } type Msg - = Switch + = Switch update : Msg -> Model -> Model update msg model = - case msg of - Switch -> { model | active = not model.active } + case msg of + Switch -> + { model | active = not model.active } @@ -42,7 +44,8 @@ update msg model = border : LineStyle -border = solid verythin <| uniform black +border = + solid verythin <| uniform black @@ -51,8 +54,8 @@ border = solid verythin <| uniform black txt : Collage Msg txt = - fromString "Hello collage!" - |> rendered + fromString "Hello collage!" + |> rendered @@ -61,35 +64,36 @@ txt = elps : Model -> Collage Msg elps model = - ellipse 100 50 - |> styled - ( uniform <| - if model.active then - lightPurple - else - lightBlue - , border - ) - |> rotate (degrees -30) - |> onClick Switch + ellipse 100 50 + |> styled + ( uniform <| + if model.active then + lightPurple + + else + lightBlue + , border + ) + |> rotate (degrees -30) + |> onClick Switch rect : Collage msg rect = - roundedRectangle 200 250 20 - |> styled ( uniform lightOrange, border ) + roundedRectangle 200 250 20 + |> styled ( uniform lightOrange, border ) tria : Collage msg tria = - triangle 100 - |> styled ( uniform lightGreen, border ) + triangle 100 + |> styled ( uniform lightGreen, border ) penta : Collage msg penta = - ngon 5 100 - |> styled ( uniform lightCharcoal, border ) + ngon 5 100 + |> styled ( uniform lightCharcoal, border ) @@ -98,8 +102,8 @@ penta = alignments : Collage msg alignments = - horizontal <| - List.map (showOrigin << align top) [ rect, tria, rect, rect ] + horizontal <| + List.map (showOrigin << align top) [ rect, tria, rect, rect ] @@ -108,21 +112,22 @@ alignments = view : Model -> Html Msg view model = - vertical - [ horizontal - [ rect - , vertical - [ tria - , tria |> rotate pi + vertical + [ horizontal + [ rect + , vertical + [ tria + , tria |> rotate pi + ] + |> center + , debug penta ] - |> center - , debug penta + , stack [ showEnvelope txt, elps model ] ] - , stack [ showEnvelope txt, elps model ] - ] - |> debug - |> svg + |> debug + |> svg main : Program () Model Msg -main = Browser.sandbox { init = init, view = view, update = update } +main = + Browser.sandbox { init = init, view = view, update = update } diff --git a/examples/Dimensions.elm b/examples/Dimensions.elm index 82f393b..8a894a4 100644 --- a/examples/Dimensions.elm +++ b/examples/Dimensions.elm @@ -9,44 +9,49 @@ import Html exposing (Html) gap : Collage msg -gap = spacer 50 50 +gap = + spacer 50 50 diamond : String -> Collage msg diamond label = - let - text = - fromString label - |> Text.shape Italic - |> rendered - w = width text - points = - [ ( 0, 20 ) - , ( -20, 0 ) - , ( 0, -20 ) - , ( w, -20 ) - , ( w + 20, 0 ) - , ( w, 20 ) - ] - shape = - polygon points - |> styled - ( uniform (Color.rgb255 255 202 255) - , solid thin (uniform Color.black) - ) - |> center - in - stack - [ text - , shape - ] + let + text = + fromString label + |> Text.shape Italic + |> rendered + + w = + width text + + points = + [ ( 0, 20 ) + , ( -20, 0 ) + , ( 0, -20 ) + , ( w, -20 ) + , ( w + 20, 0 ) + , ( w, 20 ) + ] + + shape = + polygon points + |> styled + ( uniform (Color.rgb255 255 202 255) + , solid thin (uniform Color.black) + ) + |> center + in + stack + [ text + , shape + ] main : Html msg main = - vertical - [ diamond "a very long piece of text" - , gap - , diamond "short text" - ] - |> svg + vertical + [ diamond "a very long piece of text" + , gap + , diamond "short text" + ] + |> svg diff --git a/examples/Embedding.elm b/examples/Embedding.elm index 6cdc884..fa8bb6e 100644 --- a/examples/Embedding.elm +++ b/examples/Embedding.elm @@ -9,14 +9,14 @@ import Html exposing (Html, text) main : Html msg main = - stack - [ Html.div [] - [ Html.button [] - [ text "Hello Html!" ] + stack + [ Html.div [] + [ Html.button [] + [ text "Hello Html!" ] + ] + |> html ( 100, 100 ) + , rectangle 100 100 + |> filled (uniform Color.lightGreen) ] - |> html ( 100, 100 ) - , rectangle 100 100 - |> filled (uniform Color.lightGreen) - ] - |> rotate (degrees 30) - |> svg + |> rotate (degrees 30) + |> svg diff --git a/examples/Flowchart.elm b/examples/Flowchart.elm index fbf16d4..5bc8dc6 100644 --- a/examples/Flowchart.elm +++ b/examples/Flowchart.elm @@ -14,30 +14,30 @@ import List exposing (head) type Flow - = Finish - | Task String - | Sequence Flow Flow - | Choice String Flow Flow - | Parallel (List Flow) + = Finish + | Task String + | Sequence Flow Flow + | Choice String Flow Flow + | Parallel (List Flow) example : Flow example = - Sequence - (Sequence (Task "check diff") - (Choice "diff is as whished" - (Sequence - (Parallel - [ Task "prepare changelog" - , Task "bump version" - ] - ) - (Task "publish") + Sequence + (Sequence (Task "check diff") + (Choice "diff is as whished" + (Sequence + (Parallel + [ Task "prepare changelog" + , Task "bump version" + ] + ) + (Task "publish") + ) + (Task "work harder") + ) ) - (Task "work harder") - ) - ) - Finish + Finish @@ -45,98 +45,112 @@ example = unit : Float -unit = 30 +unit = + 30 space : Collage msg -space = spacer unit unit +space = + spacer unit unit thinline : LineStyle thinline = - { defaultLineStyle - | thickness = thin - , cap = Padded - } + { defaultLineStyle + | thickness = thin + , cap = Padded + } thickline : LineStyle thickline = - { defaultLineStyle - | thickness = ultrathick - , cap = Padded - } + { defaultLineStyle + | thickness = ultrathick + , cap = Padded + } diamond : String -> Collage msg diamond label = - let - text = - fromString label - |> Text.shape Italic - |> rendered - w = width text - l = unit / 2 - points = - [ ( 0, l ) - , ( -l, 0 ) - , ( 0, -l ) - , ( w, -l ) - , ( w + l, 0 ) - , ( w, l ) - ] - shape = - polygon points - |> styled - ( uniform lightPurple - , thinline - ) - |> center - in - impose text shape + let + text = + fromString label + |> Text.shape Italic + |> rendered + + w = + width text + + l = + unit / 2 + + points = + [ ( 0, l ) + , ( -l, 0 ) + , ( 0, -l ) + , ( w, -l ) + , ( w + l, 0 ) + , ( w, l ) + ] + + shape = + polygon points + |> styled + ( uniform lightPurple + , thinline + ) + |> center + in + impose text shape box : String -> Collage msg box label = - let - text = - fromString label - |> rendered - w = width text + l - l = unit - shape = - rectangle w l - |> styled - ( uniform lightBlue - , thinline - ) - in - impose text shape + let + text = + fromString label + |> rendered + + w = + width text + l + + l = + unit + + shape = + rectangle w l + |> styled + ( uniform lightBlue + , thinline + ) + in + impose text shape dot : Collage msg dot = - circle (unit / 3) - |> styled - ( uniform green - , thinline - ) + circle (unit / 3) + |> styled + ( uniform green + , thinline + ) arrow : Float -> Collage msg arrow length = - let - body = - line length - |> traced thinline - |> rotate (pi / 2) - tip = - triangle (unit / 3) - |> filled (uniform black) - |> rotate pi - in - --FIXME: add markers - vertical [ body, tip ] + let + body = + line length + |> traced thinline + |> rotate (pi / 2) + + tip = + triangle (unit / 3) + |> filled (uniform black) + |> rotate pi + in + --FIXME: add markers + vertical [ body, tip ] @@ -145,90 +159,103 @@ arrow length = render : Flow -> Collage msg render flow = - let - addBottomArrow max flow_ = - vertical - [ flow_ - , arrow (max - height flow_) - ] - addBottomLine max flow_ = - vertical - [ flow_ - , line (max - height flow_) - |> traced thinline - |> rotate (pi / 2) - ] - branches finishing flows = - let - prerendered = - flows - |> List.map render - h = - prerendered - |> group - |> height - --NOTE: this is the length of a normal arrow - |> (+) unit - in - prerendered - |> List.map (finishing h) - |> List.intersperse space - |> horizontal - |> center - in - case flow of - Finish -> - vertical - [ arrow unit - , dot - ] - Task string -> - vertical - [ arrow unit - , box string - ] - Sequence flow1 flow2 -> - vertical - [ render flow1 - , render flow2 - ] - Choice condition left right -> - let - ( leftBranch, rightBranch ) = ( render left, render right ) - maxHeight = max (height leftBranch) (height rightBranch) + unit - inner = - horizontal - [ leftBranch - |> addBottomLine maxHeight - |> name "leftBranch" - , space - , rightBranch - |> addBottomLine maxHeight - |> name "rightBranch" - ] - |> shift ( -(envelope Right leftBranch + unit + envelope Left rightBranch) / 2, 0 ) - in - vertical - [ arrow unit - , inner - |> connect [ ( "leftBranch", top ), ( "rightBranch", top ) ] thinline - |> connect [ ( "leftBranch", bottom ), ( "rightBranch", bottom ) ] thinline - |> at top (diamond condition) - |> at bottom (diamond "") - ] - Parallel flows -> - let - inner = branches addBottomArrow flows - bar = - line (width inner + unit) - |> traced thickline - in - vertical - [ arrow unit - , bar - , inner - , bar - ] + let + addBottomArrow max flow_ = + vertical + [ flow_ + , arrow (max - height flow_) + ] + + addBottomLine max flow_ = + vertical + [ flow_ + , line (max - height flow_) + |> traced thinline + |> rotate (pi / 2) + ] + + branches finishing flows = + let + prerendered = + flows + |> List.map render + + h = + prerendered + |> group + |> height + --NOTE: this is the length of a normal arrow + |> (+) unit + in + prerendered + |> List.map (finishing h) + |> List.intersperse space + |> horizontal + |> center + in + case flow of + Finish -> + vertical + [ arrow unit + , dot + ] + + Task string -> + vertical + [ arrow unit + , box string + ] + + Sequence flow1 flow2 -> + vertical + [ render flow1 + , render flow2 + ] + + Choice condition left right -> + let + ( leftBranch, rightBranch ) = + ( render left, render right ) + + maxHeight = + max (height leftBranch) (height rightBranch) + unit + + inner = + horizontal + [ leftBranch + |> addBottomLine maxHeight + |> name "leftBranch" + , space + , rightBranch + |> addBottomLine maxHeight + |> name "rightBranch" + ] + |> shift ( -(envelope Right leftBranch + unit + envelope Left rightBranch) / 2, 0 ) + in + vertical + [ arrow unit + , inner + |> connect [ ( "leftBranch", top ), ( "rightBranch", top ) ] thinline + |> connect [ ( "leftBranch", bottom ), ( "rightBranch", bottom ) ] thinline + |> at top (diamond condition) + |> at bottom (diamond "") + ] + + Parallel flows -> + let + inner = + branches addBottomArrow flows + + bar = + line (width inner + unit) + |> traced thickline + in + vertical + [ arrow unit + , bar + , inner + , bar + ] @@ -237,5 +264,5 @@ render flow = main : Html msg main = - render example - |> svg + render example + |> svg diff --git a/examples/House.elm b/examples/House.elm index d92d87f..abcf770 100644 --- a/examples/House.elm +++ b/examples/House.elm @@ -14,21 +14,22 @@ import Html exposing (Html) type alias Model = - { hover : Part } + { hover : Part } type Part - = None - | Roof - | Chimney - | Smoke - | Wall - | Door - | Handle + = None + | Roof + | Chimney + | Smoke + | Wall + | Door + | Handle init : Model -init = { hover = None } +init = + { hover = None } @@ -36,12 +37,12 @@ init = { hover = None } type alias Msg = - Part + Part update : Msg -> Model -> Model update msg model = - { hover = msg } + { hover = msg } @@ -50,53 +51,67 @@ update msg model = house : Model -> Collage Msg house model = - let - interactive : Part -> FillStyle -> Shape -> Collage Msg - interactive part fill shape = - shape - |> filled - (if model.hover == part then - uniform purple - else - fill - ) - |> onMouseEnter (always part) - --TODO: add `lengthen 0.75` - roof = interactive Roof (uniform blue) (triangle 1) - door = interactive Door (uniform red) (rectangle 0.2 0.4) - handle = interactive Handle (uniform black) (circle 0.02) - wall = interactive Wall (uniform yellow) (square 1) - chimney = interactive Chimney (uniform green) (rectangle 0.1 0.4) - smoke = - let - puff p = - interactive Smoke (uniform gray) (circle 0.05) - |> shift p - puffs = List.map puff [ ( 0, 0 ), ( 0.05, 0.15 ) ] - in - stack puffs - in - vertical - [ stack - [ roof - , chimney - |> at (top >> (\( x, y ) -> ( x, y + 0.15 ))) smoke - |> shift ( 0.25, 0 ) + let + interactive : Part -> FillStyle -> Shape -> Collage Msg + interactive part fill shape = + shape + |> filled + (if model.hover == part then + uniform purple + + else + fill + ) + |> onMouseEnter (always part) + + --TODO: add `lengthen 0.75` + roof = + interactive Roof (uniform blue) (triangle 1) + + door = + interactive Door (uniform red) (rectangle 0.2 0.4) + + handle = + interactive Handle (uniform black) (circle 0.02) + + wall = + interactive Wall (uniform yellow) (square 1) + + chimney = + interactive Chimney (uniform green) (rectangle 0.1 0.4) + + smoke = + let + puff p = + interactive Smoke (uniform gray) (circle 0.05) + |> shift p + + puffs = + List.map puff [ ( 0, 0 ), ( 0.05, 0.15 ) ] + in + stack puffs + in + vertical + [ stack + [ roof + , chimney + |> at (top >> (\( x, y ) -> ( x, y + 0.15 ))) smoke + |> shift ( 0.25, 0 ) + ] + |> center + , stack + [ handle |> shift ( 0.05, 0.2 ) + , door |> align bottom + , wall |> align bottom + ] ] - |> center - , stack - [ handle |> shift ( 0.05, 0.2 ) - , door |> align bottom - , wall |> align bottom - ] - ] view : Model -> Html Msg view model = - house model - |> scale 200 - |> svg + house model + |> scale 200 + |> svg @@ -105,11 +120,11 @@ view model = main : Program () Model Msg main = - Browser.sandbox - { init = init - , view = view - , update = update - } + Browser.sandbox + { init = init + , view = view + , update = update + } diff --git a/examples/Lines.elm b/examples/Lines.elm index 3bbe3bb..bd9c3b5 100644 --- a/examples/Lines.elm +++ b/examples/Lines.elm @@ -9,29 +9,30 @@ import Html exposing (Html) hline : Float -> Collage msg hline t = - line 100 - |> traced (solid t (uniform black)) + line 100 + |> traced (solid t (uniform black)) gap : Collage msg -gap = spacer 50 50 +gap = + spacer 50 50 lines : Collage msg lines = - vertical <| - List.intersperse gap <| - List.map hline [ ultrathin, verythin, thin, semithick, thick, verythick, ultrathick ] + vertical <| + List.intersperse gap <| + List.map hline [ ultrathin, verythin, thin, semithick, thick, verythick, ultrathick ] main : Html msg main = - horizontal - [ gap - , vertical + horizontal [ gap - , lines - , gap + , vertical + [ gap + , lines + , gap + ] ] - ] - |> svg + |> svg diff --git a/examples/Names.elm b/examples/Names.elm index dbac7df..609c52c 100644 --- a/examples/Names.elm +++ b/examples/Names.elm @@ -14,56 +14,58 @@ import Html exposing (Html, text) rect1 : Collage msg rect1 = - square 7 - |> filled (uniform brown) - |> scale 10 - |> name "rect1" + square 7 + |> filled (uniform brown) + |> scale 10 + |> name "rect1" rect2 : Collage msg rect2 = - rectangle 5 7 - |> filled (uniform green) - |> scale 10 - |> rotate (degrees 30) - |> name "rect2" + rectangle 5 7 + |> filled (uniform green) + |> scale 10 + |> rotate (degrees 30) + |> name "rect2" rect3 : Collage msg rect3 = - rectangle 100 50 - |> filled (uniform purple) - |> name "rect3" + rectangle 100 50 + |> filled (uniform purple) + |> name "rect3" circ : Collage msg circ = - circle 50 - |> filled (uniform blue) - |> name "circ" + circle 50 + |> filled (uniform blue) + |> name "circ" mark : Collage msg mark = - circle 3 - |> filled (uniform yellow) - |> name "mark" + circle 3 + |> filled (uniform yellow) + |> name "mark" collage : Collage msg collage = - let - pos = - locate "circ" topRight circ - |> Maybe.withDefault ( 0, 0 ) - inner = - mark - |> shift pos - background = - --NOTE: rect2 and rect3 have the same name, rect2 should be found first. - horizontal [ rect1, rect2, rect3 ] - in - background + let + pos = + locate "circ" topRight circ + |> Maybe.withDefault ( 0, 0 ) + + inner = + mark + |> shift pos + + background = + --NOTE: rect2 and rect3 have the same name, rect2 should be found first. + horizontal [ rect1, rect2, rect3 ] + in + background @@ -72,19 +74,19 @@ collage = main : Html msg main = - Html.div [] - [ svg collage - , Html.p [] - [ { names = - names collage - , rectTopRight = - locate "rect" topRight collage - , rect = - Core.search (.name >> Maybe.map ((==) "rect") >> Maybe.withDefault False) collage - , levels = - List.map .name <| Core.levels collage - } - |> Debug.toString - |> text + Html.div [] + [ svg collage + , Html.p [] + [ { names = + names collage + , rectTopRight = + locate "rect" topRight collage + , rect = + Core.search (.name >> Maybe.map ((==) "rect") >> Maybe.withDefault False) collage + , levels = + List.map .name <| Core.levels collage + } + |> Debug.toString + |> text + ] ] - ] diff --git a/examples/Path.elm b/examples/Path.elm index 8064965..12674f8 100644 --- a/examples/Path.elm +++ b/examples/Path.elm @@ -8,9 +8,10 @@ import Html exposing (Html) zigzag : Collage msg zigzag = - path [ ( 0, 50 ), ( 50, 0 ), ( 50, 50 ) ] - |> traced (solid thin (uniform red)) + path [ ( 0, 50 ), ( 50, 0 ), ( 50, 50 ) ] + |> traced (solid thin (uniform red)) main : Html msg -main = zigzag |> svg +main = + zigzag |> svg diff --git a/examples/Sierpinski.elm b/examples/Sierpinski.elm index faefc86..8b27123 100644 --- a/examples/Sierpinski.elm +++ b/examples/Sierpinski.elm @@ -9,24 +9,26 @@ import Html exposing (Html) sierpinski : Int -> Float -> Collage msg sierpinski n side = - case n of - 0 -> - triangle side - |> filled (uniform Color.blue) - _ -> - let - smaller = sierpinski (n - 1) side - in - vertical - [ smaller - , horizontal [ smaller, smaller ] |> center - ] + case n of + 0 -> + triangle side + |> filled (uniform Color.blue) + + _ -> + let + smaller = + sierpinski (n - 1) side + in + vertical + [ smaller + , horizontal [ smaller, smaller ] |> center + ] main : Html msg main = - sierpinski 5 10 - |> svg + sierpinski 5 10 + |> svg diff --git a/examples/Simple.elm b/examples/Simple.elm index 60b9e48..de3701b 100644 --- a/examples/Simple.elm +++ b/examples/Simple.elm @@ -9,14 +9,15 @@ import Html exposing (Html) main : Html msg main = - let - circ = - circle 50 - |> filled (uniform Color.red) - rect = - rectangle 200 100 - |> filled (uniform Color.blue) - in - rect - |> at topLeft circ - |> svg + let + circ = + circle 50 + |> filled (uniform Color.red) + + rect = + rectangle 200 100 + |> filled (uniform Color.blue) + in + rect + |> at topLeft circ + |> svg diff --git a/examples/elm.json b/examples/elm.json index 1c522e9..34a44a6 100644 --- a/examples/elm.json +++ b/examples/elm.json @@ -4,7 +4,7 @@ ".", "../src/" ], - "elm-version": "0.19.0", + "elm-version": "0.19.1", "dependencies": { "direct": { "avh4/elm-color": "1.0.0", @@ -25,4 +25,4 @@ "direct": {}, "indirect": {} } -} \ No newline at end of file +} diff --git a/src/Collage.elm b/src/Collage.elm index 0fec202..700d404 100644 --- a/src/Collage.elm +++ b/src/Collage.elm @@ -1,20 +1,20 @@ module Collage exposing - ( Point, opposite, Collage - , shift, shiftX, shiftY, scale, scaleX, scaleY, rotate, opacity - , group - , Shape, rectangle, square, roundedRectangle, roundedSquare, ellipse, circle, polygon, ngon, triangle - , filled, outlined, styled - , Path, line, segment, path - , traced, close - , rendered - , image, html - , Style - , FillStyle, transparent, uniform - , LineStyle, invisible, defaultLineStyle - , solid, broken, dot, dash, longdash, dashdot - , ultrathin, verythin, thin, semithick, thick, verythick, ultrathick - , LineCap(..), LineJoin(..) - ) + ( Point, opposite, Collage + , shift, shiftX, shiftY, scale, scaleX, scaleY, rotate, opacity + , group + , Shape, rectangle, square, roundedRectangle, roundedSquare, ellipse, circle, polygon, ngon, triangle + , filled, outlined, styled + , Path, line, segment, path + , traced, close + , rendered + , image, html + , Style + , FillStyle, transparent, uniform + , LineStyle, invisible, defaultLineStyle + , solid, broken, dot, dash, longdash, dashdot + , ultrathin, verythin, thin, semithick, thick, verythick, ultrathick + , LineCap(..), LineJoin(..) + ) {-| The collage module is here to help you create freeform graphics. You can style all sorts of forms including shapes, paths, text, and images, @@ -236,7 +236,7 @@ import Html exposing (Html) a point in the center of the canvas. -} type alias Point = - ( Float, Float ) + ( Float, Float ) {-| Calculate the point at the opposite side of the origin. @@ -244,12 +244,12 @@ type alias Point = Simply negates the coordinates: opposite ( x, y ) = - ( -x, -y ) + ( -x, -y ) -} opposite : Point -> Point opposite ( x, y ) = - ( -x, -y ) + ( -x, -y ) @@ -260,7 +260,7 @@ opposite ( x, y ) = A collage could be a red circle, a dotted line, a chunk of text, or an arbitrary Html element. -} type alias Collage msg = - Core.Collage Core.FillStyle LineStyle Text.Style msg + Core.Collage Core.FillStyle LineStyle Text.Style msg @@ -271,12 +271,13 @@ type alias Collage msg = which again can be shifted, rotated, scaled, etc. group [ drawing1, drawing2, drawing3 ] - |> scale 3 - |> rotate (degrees 90) + |> scale 3 + |> rotate (degrees 90) -} group : List (Collage msg) -> Collage msg -group = Core.collage << Core.Group +group = + Core.collage << Core.Group @@ -292,7 +293,7 @@ This is a relative translation, so collage - |> shift ( 5, 10 ) + |> shift ( 5, 10 ) would shift `collage` five pixels to the right and ten pixels up. @@ -303,30 +304,33 @@ Shifting a collage with `(5,10)` is equivalent to moving its local origin with ` -} shift : ( Float, Float ) -> Collage msg -> Collage msg shift ( dx, dy ) collage = - let - ( x, y ) = collage.shift - in - { collage | shift = ( x + dx, y + dy ) } + let + ( x, y ) = + collage.shift + in + { collage | shift = ( x + dx, y + dy ) } {-| Shift a collage by the given amount on the X axis within its local space. -} shiftX : Float -> Collage msg -> Collage msg shiftX dx collage = - let - ( x, y ) = collage.shift - in - { collage | shift = ( x + dx, y ) } + let + ( x, y ) = + collage.shift + in + { collage | shift = ( x + dx, y ) } {-| Shift a collage by the given amount on the Y axis within its local space. -} shiftY : Float -> Collage msg -> Collage msg shiftY dy collage = - let - ( x, y ) = collage.shift - in - { collage | shift = ( x, y + dy ) } + let + ( x, y ) = + collage.shift + in + { collage | shift = ( x, y + dy ) } {-| Scale a collage by a given factor. @@ -336,7 +340,7 @@ Scaling by 2 doubles both dimensions and quadruples the area. -} scale : Float -> Collage msg -> Collage msg scale s collage = - scaleXY ( s, s ) collage + scaleXY ( s, s ) collage {-| Scale a collage horizontally (in its local space) by a given factor. @@ -346,7 +350,7 @@ Scaling by 2 doubles the width and doubles the area. -} scaleX : Float -> Collage msg -> Collage msg scaleX s collage = - scaleXY ( s, 1 ) collage + scaleXY ( s, 1 ) collage {-| Scale a collage vertically (in its local space) by a given factor. @@ -356,15 +360,16 @@ Scaling by 2 doubles the height and doubles the area. -} scaleY : Float -> Collage msg -> Collage msg scaleY s collage = - scaleXY ( 1, s ) collage + scaleXY ( 1, s ) collage scaleXY : ( Float, Float ) -> Collage msg -> Collage msg scaleXY ( sx, sy ) collage = - let - ( sx0, sy0 ) = collage.scale - in - { collage | scale = ( sx0 * sx, sy0 * sy ) } + let + ( sx0, sy0 ) = + collage.scale + in + { collage | scale = ( sx0 * sx, sy0 * sy ) } {-| Rotate a collage by a given angle. @@ -375,12 +380,12 @@ and turns things **counterclockwise**. So to turn `collage` 30° to the left you would say: collage - |> rotate (degrees 30) + |> rotate (degrees 30) -} rotate : Float -> Collage msg -> Collage msg rotate t collage = - { collage | rotation = collage.rotation + t } + { collage | rotation = collage.rotation + t } {-| Set the opacity of a collage. @@ -390,7 +395,7 @@ The default is 1, and 0 is totally transparent. -} opacity : Float -> Collage msg -> Collage msg opacity a collage = - { collage | opacity = a } + { collage | opacity = a } @@ -404,7 +409,7 @@ Position, color, thickness, etc. are all specified later. -} type alias Shape = - Core.Shape + Core.Shape @@ -422,7 +427,8 @@ so the given list of points does not need to start and end with the same positio -} polygon : List Point -> Shape -polygon = Core.Polygon +polygon = + Core.Polygon {-| A regular polygon with _n_ sides. @@ -439,13 +445,17 @@ Some ngon's with radius 50: -} ngon : Int -> Float -> Shape ngon n r = - let - m = toFloat n - t = 2 * pi / m - f i = - ( r * cos (t * toFloat i + pi / 2), r * sin (t * toFloat i + pi / 2) ) - in - polygon <| List.map f (List.range 0 n) + let + m = + toFloat n + + t = + 2 * pi / m + + f i = + ( r * cos (t * toFloat i + pi / 2), r * sin (t * toFloat i + pi / 2) ) + in + polygon <| List.map f (List.range 0 n) {-| An equilateral triangle pointing upwards with given base. @@ -464,18 +474,21 @@ however: -} triangle : Float -> Shape triangle b = - let - x = b / 2 - y = sqrt 3 / 2 * x - in - polygon [ ( -x, -y ), ( x, -y ), ( 0, y ) ] + let + x = + b / 2 + + y = + sqrt 3 / 2 * x + in + polygon [ ( -x, -y ), ( x, -y ), ( 0, y ) ] {-| A rectangle of given width and height. -} rectangle : Float -> Float -> Shape rectangle w h = - roundedRectangle w h 0 + roundedRectangle w h 0 {-| A square of given size. @@ -487,7 +500,7 @@ Of course this is equal to using `rectangle` with the same width and height: -} square : Float -> Shape square size = - rectangle size size + rectangle size size {-| A rectangle with rounded corners. @@ -497,7 +510,8 @@ last is the radius of the corners. -} roundedRectangle : Float -> Float -> Float -> Shape -roundedRectangle = Core.Rectangle +roundedRectangle = + Core.Rectangle {-| A square with rounded corners. @@ -509,7 +523,7 @@ Of course this is equal to using `roundedRectangle` with the same width and heig -} roundedSquare : Float -> Float -> Shape roundedSquare size = - roundedRectangle size size + roundedRectangle size size {-| An ellipse with given horizontal and vertical radii. @@ -523,7 +537,8 @@ roundedSquare size = -} ellipse : Float -> Float -> Shape -ellipse = Core.Ellipse +ellipse = + Core.Ellipse {-| A circle of given radius. @@ -534,7 +549,8 @@ As with a square, using `circle` is the same as using `ellipse` with the same x -} circle : Float -> Shape -circle = Core.Circle +circle = + Core.Circle @@ -548,14 +564,14 @@ The **outline is left invisible**. To draw a red circle of radius 50 you say: circle 50 - |> filled (uniform red) + |> filled (uniform red) See below for possible fill styles. -} filled : FillStyle -> Shape -> Collage msg filled fill = - styled ( fill, invisible ) + styled ( fill, invisible ) {-| Adds an outline to a shape, turning it into a collage. @@ -565,14 +581,14 @@ The **fill is left transparent**. To draw a square with edge length 30 with a thin black dashed outline you say: square 30 - |> outlined (dot thin (uniform black)) + |> outlined (dot thin (uniform black)) See below for the possible line styles. -} outlined : LineStyle -> Shape -> Collage msg outlined linestyle = - styled ( transparent, linestyle ) + styled ( transparent, linestyle ) {-| Adds a fill and an outline to a shape, turning it into a collage. @@ -581,10 +597,10 @@ The tuple argument contains a fill style and a line style. To draw a thick black outlined green triangle with base 30 you say: triangle 30 - |> styled - ( uniform green - , solid thick (uniform black) - ) + |> styled + ( uniform green + , solid thick (uniform black) + ) The tuple form helps in defining your own reusable styles. For example, if you want more of you shapes to have a thick black outline, @@ -601,7 +617,7 @@ See below for all possible fill and line styles. -} styled : ( FillStyle, LineStyle ) -> Shape -> Collage msg styled style = - Core.collage << Core.Shape style + Core.collage << Core.Shape style @@ -623,7 +639,7 @@ which can be filled and outlined. -} type alias Path = - Core.Path + Core.Path @@ -637,12 +653,12 @@ The origin of the line will be `(0,0)`. Here is a thick dotted yellow horizontal line of length 20: line 20 - |> traced (dot thick (uniform yellow)) + |> traced (dot thick (uniform yellow)) -} line : Float -> Path line l = - path [ ( -l / 2, 0 ), ( l / 2, 0 ) ] + path [ ( -l / 2, 0 ), ( l / 2, 0 ) ] {-| Create a path along a given line segment. @@ -651,7 +667,7 @@ Takes the start and end points of the segment as arguments. To draw a sloped blue line from (0,5) to (5,0) you say: segment ( 0, 5 ) ( 5, 0 ) - |> traced (uniform blue) + |> traced (uniform blue) - Note: If you like to automatically position lines, @@ -662,7 +678,7 @@ To draw a sloped blue line from (0,5) to (5,0) you say: -} segment : Point -> Point -> Path segment a b = - path [ a, b ] + path [ a, b ] {-| Create a path that follows a sequence of points. @@ -671,7 +687,8 @@ It can be thought of as drawing a “connect-the-dots” line through a list of -} path : List Point -> Path -path = Core.Polyline +path = + Core.Polyline @@ -710,7 +727,7 @@ path = Core.Polyline Here is a red zig-zag: path [ ( 0, 5 ), ( 5, 0 ), ( 5, 5 ) ] - |> traced (solid thin (uniform red)) + |> traced (solid thin (uniform red)) Paths can only be traced. If you like to fill a path, @@ -719,7 +736,7 @@ you have to turn it into a shape by _closing_ it first. -} traced : LineStyle -> Path -> Collage msg traced linestyle p = - Core.collage <| Core.Path linestyle p + Core.collage <| Core.Path linestyle p {-| Close a path so that it also can be filled. @@ -733,7 +750,8 @@ If you really want this, you have two options: -} close : Path -> Shape -close = Core.Loop +close = + Core.Loop @@ -743,16 +761,16 @@ close = Core.Loop {-| Render a chunk of styled text and turn it into a collage. Text.fromString "Hello Collage!" - |> Text.shape Text.Italic - |> Text.size huge - |> rendered + |> Text.shape Text.Italic + |> Text.size huge + |> rendered See the Collage.Text module for all the possibilities to create and style text. -} rendered : Text -> Collage msg rendered text = - Core.collage <| Core.Text ( Text.width text, Text.height text ) text + Core.collage <| Core.Text ( Text.width text, Text.height text ) text @@ -766,7 +784,7 @@ rendered text = -} image : ( Float, Float ) -> String -> Collage msg image dims = - Core.collage << Core.Image dims + Core.collage << Core.Image dims {-| Create a collage from an arbitrary Html element. @@ -776,7 +794,7 @@ The resulting collage is subject to all of the regular transformations. -} html : ( Float, Float ) -> List (Html.Attribute msg) -> Html msg -> Collage msg html dims attrs = - Core.collage << Core.Html dims attrs + Core.collage << Core.Html dims attrs @@ -786,7 +804,7 @@ html dims attrs = {-| Convenience shorthand for styling. -} type alias Style = - ( FillStyle, LineStyle ) + ( FillStyle, LineStyle ) @@ -799,19 +817,21 @@ For now, it can only be a uniform color or no fill at all. -} type alias FillStyle = - Core.FillStyle + Core.FillStyle {-| Uniform color fill. -} uniform : Color -> FillStyle -uniform = Core.Uniform +uniform = + Core.Uniform {-| Transparent fill. -} transparent : FillStyle -transparent = Core.Transparent +transparent = + Core.Transparent @@ -835,13 +855,13 @@ To define a red, dashed line style with a thickness of 5px: -} type alias LineStyle = - { fill : FillStyle - , thickness : Float - , cap : LineCap - , join : LineJoin - , dashPattern : List ( Int, Int ) - , dashPhase : Int - } + { fill : FillStyle + , thickness : Float + , cap : LineCap + , join : LineJoin + , dashPattern : List ( Int, Int ) + , dashPhase : Int + } {-| The default line style, which is solid black with flat caps and sharp joints. @@ -854,25 +874,27 @@ For example, to make a thicker line, you could say: -} defaultLineStyle : LineStyle defaultLineStyle = - { fill = uniform Color.black - , thickness = thin - , cap = Flat - , join = Sharp - , dashPattern = [] - , dashPhase = 0 - } + { fill = uniform Color.black + , thickness = thin + , cap = Flat + , join = Sharp + , dashPattern = [] + , dashPhase = 0 + } {-| Invisible line. -} invisible : LineStyle -invisible = solid 0 transparent +invisible = + solid 0 transparent {-| A line style representing a solid line of given thickness and color. -} solid : Float -> FillStyle -> LineStyle -solid = broken [] +solid = + broken [] {-| A custom line defined by a list of `(on, off)` dash length: @@ -884,11 +906,11 @@ solid = broken [] -} broken : List ( Int, Int ) -> Float -> FillStyle -> LineStyle broken dashes thickness fill = - { defaultLineStyle - | fill = fill - , thickness = thickness - , dashPattern = dashes - } + { defaultLineStyle + | fill = fill + , thickness = thickness + , dashPattern = dashes + } {-| A dotted line type with the given thickness. @@ -898,10 +920,11 @@ Calculates the length of the dots based on the given line thickness. -} dot : Float -> FillStyle -> LineStyle dot thickness = - let - d = round thickness - in - broken [ ( d, d ) ] thickness + let + d = + round thickness + in + broken [ ( d, d ) ] thickness {-| A dashed line type with the given thickness. @@ -911,10 +934,11 @@ Calculates the length of the dashes based on the given line thickness. -} dash : Float -> FillStyle -> LineStyle dash thickness = - let - d = round thickness - in - broken [ ( d * 5, d * 2 ) ] thickness + let + d = + round thickness + in + broken [ ( d * 5, d * 2 ) ] thickness {-| A dashed line type with the given thickness, where the dashes are longer than normal. @@ -924,10 +948,11 @@ Calculates the length of the dashes based on the given line thickness. -} longdash : Float -> FillStyle -> LineStyle longdash thickness = - let - d = round thickness - in - broken [ ( d * 12, d * 6 ) ] thickness + let + d = + round thickness + in + broken [ ( d * 12, d * 6 ) ] thickness {-| A dashed line type with the given thickness, including alternating dots and dashes. @@ -937,10 +962,11 @@ Calculates the length of the dashes based on the given line thickness. -} dashdot : Float -> FillStyle -> LineStyle dashdot thickness = - let - d = round thickness - in - broken [ ( d * 5, d ), ( d, d ) ] thickness + let + d = + round thickness + in + broken [ ( d * 5, d ), ( d, d ) ] thickness @@ -950,43 +976,50 @@ dashdot thickness = {-| 0.5 px -} ultrathin : Float -ultrathin = 0.5 +ultrathin = + 0.5 {-| 1 px -} verythin : Float -verythin = 1.0 +verythin = + 1.0 {-| 2 px -} thin : Float -thin = 2.0 +thin = + 2.0 {-| 3 px -} semithick : Float -semithick = 3.0 +semithick = + 3.0 {-| 4 px -} thick : Float -thick = 4.0 +thick = + 4.0 {-| 6 px -} verythick : Float -verythick = 6.0 +verythick = + 6.0 {-| 8 px -} ultrathick : Float -ultrathick = 8.0 +ultrathick = + 8.0 @@ -1003,9 +1036,9 @@ In TikZ and Css these options are called butt, rect, and round. -} type LineCap - = Flat - | Round - | Padded + = Flat + | Round + | Padded @@ -1018,6 +1051,6 @@ In TikZ and Css these options have the nondescriptive names round, miter, and be -} type LineJoin - = Smooth - | Sharp - | Clipped + = Smooth + | Sharp + | Clipped diff --git a/src/Collage/Core.elm b/src/Collage/Core.elm index fa44e96..33a6e24 100644 --- a/src/Collage/Core.elm +++ b/src/Collage/Core.elm @@ -15,7 +15,7 @@ import Json.Decode as Json type alias Point = - ( Float, Float ) + ( Float, Float ) @@ -23,177 +23,222 @@ type alias Point = type alias Transform r = - { r | shift : ( Float, Float ), scale : ( Float, Float ), rotation : Float } + { r | shift : ( Float, Float ), scale : ( Float, Float ), rotation : Float } type alias Collage fill line text msg = - Transform - { opacity : Float - , name : Maybe String - , handlers : List ( String, Json.Decoder msg ) - , basic : BasicCollage fill line text msg - } + Transform + { opacity : Float + , name : Maybe String + , handlers : List ( String, Json.Decoder msg ) + , basic : BasicCollage fill line text msg + } type BasicCollage fill line text msg - = Shape ( fill, line ) Shape - | Path line Path - | Text ( Float, Float ) (Text text) - | Image ( Float, Float ) String - | Html ( Float, Float ) (List (Html.Attribute msg)) (Html msg) - --FIXME: Implement grouping as fold over stacking? - | Group (List (Collage fill line text msg)) - | Subcollage (Collage fill line text msg) (Collage fill line text msg) + = Shape ( fill, line ) Shape + | Path line Path + | Text ( Float, Float ) (Text text) + | Image ( Float, Float ) String + | Html ( Float, Float ) (List (Html.Attribute msg)) (Html msg) + --FIXME: Implement grouping as fold over stacking? + | Group (List (Collage fill line text msg)) + | Subcollage (Collage fill line text msg) (Collage fill line text msg) collage : BasicCollage fill line text msg -> Collage fill line text msg collage basic = - { shift = ( 0, 0 ) - , scale = ( 1, 1 ) - , rotation = 0 - , opacity = 1 - , name = Nothing - , handlers = [] - , basic = basic - } + { shift = ( 0, 0 ) + , scale = ( 1, 1 ) + , rotation = 0 + , opacity = 1 + , name = Nothing + , handlers = [] + , basic = basic + } apply : Transform r -> Point -> Point apply { shift, scale, rotation } = - let - ( dx, dy ) = shift - ( sx, sy ) = scale - shifted ( x, y ) = - ( x + dx, y + dy ) - scaled ( x, y ) = - ( sx * x, sy * y ) - rotated ( x, y ) = - let - c = cos rotation - s = sin rotation - in - ( c * x - s * y, s * x + c * y ) - in - shifted << scaled << rotated + let + ( dx, dy ) = + shift + + ( sx, sy ) = + scale + + shifted ( x, y ) = + ( x + dx, y + dy ) + + scaled ( x, y ) = + ( sx * x, sy * y ) + + rotated ( x, y ) = + let + c = + cos rotation + + s = + sin rotation + in + ( c * x - s * y, s * x + c * y ) + in + shifted << scaled << rotated combine : Transform r -> Transform r -> Transform r combine { shift, scale, rotation } this = - let - ( dx, dy ) = shift - ( fx, fy ) = scale - ( x, y ) = this.shift - ( sx, sy ) = this.shift - in - { this - | shift = ( x + dx, y + dy ) - , scale = ( sx * fx, sy * fy ) - , rotation = this.rotation + rotation - } + let + ( dx, dy ) = + shift + + ( fx, fy ) = + scale + + ( x, y ) = + this.shift + + ( sx, sy ) = + this.shift + in + { this + | shift = ( x + dx, y + dy ) + , scale = ( sx * fx, sy * fy ) + , rotation = this.rotation + rotation + } foldr : (Collage fill line text msg -> a -> a) -> a -> Collage fill line text msg -> a foldr f acc col = - let - foldrOf = List.foldr (\c a -> foldr f a c) acc - recurse = - case col.basic of - Group cols -> foldrOf cols - Subcollage fore back -> foldrOf [ fore, back ] - _ -> acc - in - f col recurse + let + foldrOf = + List.foldr (\c a -> foldr f a c) acc + + recurse = + case col.basic of + Group cols -> + foldrOf cols + + Subcollage fore back -> + foldrOf [ fore, back ] + + _ -> + acc + in + f col recurse foldrLazy : (Collage fill line text msg -> (() -> a) -> a) -> a -> Collage fill line text msg -> a foldrLazy f acc col = - let - foldrOf = Helpers.foldrLazy (\c a -> foldrLazy f (a ()) c) acc - recurse () = - case col.basic of - Group cols -> foldrOf cols - Subcollage fore back -> foldrOf [ fore, back ] - _ -> acc - in - f col recurse + let + foldrOf = + Helpers.foldrLazy (\c a -> foldrLazy f (a ()) c) acc + + recurse () = + case col.basic of + Group cols -> + foldrOf cols + + Subcollage fore back -> + foldrOf [ fore, back ] + + _ -> + acc + in + f col recurse foldl : (Collage fill line text msg -> a -> a) -> a -> Collage fill line text msg -> a foldl f acc col = - let - foldlOf = List.foldl (\c a -> foldl f a c) - recurse res = - case col.basic of - Group cols -> foldlOf res cols - Subcollage fore back -> foldlOf res [ fore, back ] - _ -> res - in - recurse <| f col acc + let + foldlOf = + List.foldl (\c a -> foldl f a c) + + recurse res = + case col.basic of + Group cols -> + foldlOf res cols + + Subcollage fore back -> + foldlOf res [ fore, back ] + + _ -> + res + in + recurse <| f col acc {-| Lazy depth-first search using `foldr` -} find : (Collage fill line text msg -> Bool) -> Collage fill line text msg -> Maybe (Collage fill line text msg) find p = - --NOTE: Could be defined generically on types having `foldr`. - let - f x = - if p x then - Just x - else - Nothing - in - foldrLazy (Helpers.orLazy << f) Nothing + --NOTE: Could be defined generically on types having `foldr`. + let + f x = + if p x then + Just x + + else + Nothing + in + foldrLazy (Helpers.orLazy << f) Nothing levels : Collage fill line text msg -> List (Collage fill line text msg) levels col = - let - recurse result queue = - --NOTE: This function is tail recursive :-) - case queue of - [] -> - List.reverse result - this :: rest -> - case this.basic of - Group cols -> - --NOTE: First recurse on the rest of the queue, then go for the group contents - recurse result (rest ++ cols) - Subcollage fore back -> - recurse result (rest ++ [ fore, back ]) - _ -> - --NOTE: We only add non-groups to the result - recurse (this :: result) rest - in - --NOTE: Start with the empty queue as the result and the current collage in the queue - recurse [] [ col ] + let + recurse result queue = + --NOTE: This function is tail recursive :-) + case queue of + [] -> + List.reverse result + + this :: rest -> + case this.basic of + Group cols -> + --NOTE: First recurse on the rest of the queue, then go for the group contents + recurse result (rest ++ cols) + + Subcollage fore back -> + recurse result (rest ++ [ fore, back ]) + + _ -> + --NOTE: We only add non-groups to the result + recurse (this :: result) rest + in + --NOTE: Start with the empty queue as the result and the current collage in the queue + recurse [] [ col ] {-| Breadth-first search on collages -} search : (Collage fill line text msg -> Bool) -> Collage fill line text msg -> Maybe (Collage fill line text msg) search pred col = - let - recurse queue = - case queue of - [] -> - Nothing - this :: rest -> - if pred this then - --NOTE: We found it! - Just this - else - --NOTE: We go on with our search - case this.basic of - Group cols -> - --NOTE: First recurse on the rest of the queue, then go for the group contents - recurse (rest ++ cols) - Subcollage fore back -> - recurse (rest ++ [ fore, back ]) - _ -> - recurse rest - in - recurse [ col ] + let + recurse queue = + case queue of + [] -> + Nothing + + this :: rest -> + if pred this then + --NOTE: We found it! + Just this + + else + --NOTE: We go on with our search + case this.basic of + Group cols -> + --NOTE: First recurse on the rest of the queue, then go for the group contents + recurse (rest ++ cols) + + Subcollage fore back -> + recurse (rest ++ [ fore, back ]) + + _ -> + recurse rest + in + recurse [ col ] @@ -201,22 +246,22 @@ search pred col = type Shape - = Polygon (List Point) - --NOTE: Although Rectangles are a special case of Polygons, they can have rounded corners, so we have a separate constructor for them. - --NOTE: Squares are just Rectangles with the same width and height, therefore we don't need them here. - | Rectangle Float Float Float - | Ellipse Float Float - --NOTE: Circles are just Elipses with the same x- and y-radius, so we could just use Ellipse, but it eases the calculation of envelopes. - | Circle Float - | Loop Path + = Polygon (List Point) + --NOTE: Although Rectangles are a special case of Polygons, they can have rounded corners, so we have a separate constructor for them. + --NOTE: Squares are just Rectangles with the same width and height, therefore we don't need them here. + | Rectangle Float Float Float + | Ellipse Float Float + --NOTE: Circles are just Elipses with the same x- and y-radius, so we could just use Ellipse, but it eases the calculation of envelopes. + | Circle Float + | Loop Path type Path - = Polyline (List Point) + = Polyline (List Point) type Text style - = Chunk style String + = Chunk style String @@ -224,8 +269,8 @@ type Text style type FillStyle - = Transparent - --TODO: Add gradient and pattern fills - -- | Gradient Gradient - -- | Pattern Float Float String Float - | Uniform Color + = Transparent + --TODO: Add gradient and pattern fills + -- | Gradient Gradient + -- | Pattern Float Float String Float + | Uniform Color diff --git a/src/Collage/Events.elm b/src/Collage/Events.elm index b3f30d2..a0a0447 100644 --- a/src/Collage/Events.elm +++ b/src/Collage/Events.elm @@ -1,20 +1,20 @@ module Collage.Events exposing - ( onClick, onDoubleClick, onMouseDown, onMouseUp, onMouseMove, onMouseEnter, onMouseLeave, onMouseOver, onMouseOut - , onFocusIn, onFocusOut - , on - ) + ( onClick, onDoubleClick, onMouseDown, onMouseUp, onMouseMove, onMouseEnter, onMouseLeave, onMouseOver, onMouseOut + , onFocusIn, onFocusOut + , on + ) {-| Use this module to make your graphics interactive. It is as easy as you think it is. collage - |> onClick Clicked + |> onClick Clicked Will send the message `Clicked` to your update function where you can handle it. You will probably need some way to identify your objects to keep track of _which_ object the user clicked on: drawing.collage - |> onClick (ClickedOn drawing.id) + |> onClick (ClickedOn drawing.id) where `drawing : { r | collage : Collage, id : Id }` @@ -60,79 +60,90 @@ the resulting message will be passed along to your `update` function. onClick : msg -> Collage msg -> Collage msg onClick msg = - on "click" (Json.succeed msg) + on "click" (Json.succeed msg) -} on : String -> Json.Decoder msg -> Collage msg -> Collage msg on event decoder collage = - { collage | handlers = ( event, decoder ) :: collage.handlers } + { collage | handlers = ( event, decoder ) :: collage.handlers } simpleOn : String -> msg -> Collage msg -> Collage msg simpleOn event = - on event << Json.succeed + on event << Json.succeed mouseOn : String -> (Point -> msg) -> Collage msg -> Collage msg mouseOn event msg = - on event <| - Json.map msg <| - Json.map2 - (\x y -> ( x, y )) - (field "clientX" Json.float) - (field "clientY" Json.float) + on event <| + Json.map msg <| + Json.map2 + (\x y -> ( x, y )) + (field "clientX" Json.float) + (field "clientY" Json.float) {-| -} onClick : msg -> Collage msg -> Collage msg -onClick = simpleOn "click" +onClick = + simpleOn "click" {-| -} onDoubleClick : msg -> Collage msg -> Collage msg -onDoubleClick = simpleOn "dblclick" +onDoubleClick = + simpleOn "dblclick" {-| -} onMouseDown : (Point -> msg) -> Collage msg -> Collage msg -onMouseDown = mouseOn "mousedown" +onMouseDown = + mouseOn "mousedown" {-| -} onMouseUp : (Point -> msg) -> Collage msg -> Collage msg -onMouseUp = mouseOn "mouseup" +onMouseUp = + mouseOn "mouseup" {-| -} onMouseEnter : (Point -> msg) -> Collage msg -> Collage msg -onMouseEnter = mouseOn "mouseenter" +onMouseEnter = + mouseOn "mouseenter" {-| -} onMouseLeave : (Point -> msg) -> Collage msg -> Collage msg -onMouseLeave = mouseOn "mouseleave" +onMouseLeave = + mouseOn "mouseleave" {-| -} onMouseOver : (Point -> msg) -> Collage msg -> Collage msg -onMouseOver = mouseOn "mouseover" +onMouseOver = + mouseOn "mouseover" {-| -} onMouseOut : (Point -> msg) -> Collage msg -> Collage msg -onMouseOut = mouseOn "mouseout" +onMouseOut = + mouseOn "mouseout" {-| -} onMouseMove : (Point -> msg) -> Collage msg -> Collage msg -onMouseMove = mouseOn "mousemove" +onMouseMove = + mouseOn "mousemove" {-| -} onFocusIn : msg -> Collage msg -> Collage msg -onFocusIn = simpleOn "focusin" +onFocusIn = + simpleOn "focusin" {-| -} onFocusOut : msg -> Collage msg -> Collage msg -onFocusOut = simpleOn "focusout" +onFocusOut = + simpleOn "focusout" diff --git a/src/Collage/Layout.elm b/src/Collage/Layout.elm index b0af3bd..16f8f97 100644 --- a/src/Collage/Layout.elm +++ b/src/Collage/Layout.elm @@ -1,12 +1,12 @@ module Collage.Layout exposing - ( envelope, Direction(..), facing, distances, Distances, width, height - , horizontal, vertical, stack, impose, beside, place - , spacer, empty - , align, at, center - , Anchor, top, topRight, right, bottomRight, bottom, bottomLeft, left, topLeft, base - , name, locate, connect, names - , showOrigin, showEnvelope, debug - ) + ( envelope, Direction(..), facing, distances, Distances, width, height + , horizontal, vertical, stack, impose, beside, place + , spacer, empty + , align, at, center + , Anchor, top, topRight, right, bottomRight, bottom, bottomLeft, left, topLeft, base + , name, locate, connect, names + , showOrigin, showEnvelope, debug + ) {-| With this module, you can compose collages in a more automatic way. Instead of shifting collages manually, @@ -133,10 +133,10 @@ import Maybe exposing (withDefault) {-| The four different directions in which we can calculate an envelope. -} type Direction - = Up - | Down - | Right - | Left + = Up + | Down + | Right + | Left {-| Calculate the facing direction. @@ -148,11 +148,18 @@ type Direction -} facing : Direction -> Direction facing dir = - case dir of - Up -> Down - Down -> Up - Right -> Left - Left -> Right + case dir of + Up -> + Down + + Down -> + Up + + Right -> + Left + + Left -> + Right @@ -181,14 +188,22 @@ The same holds for the other three directions. -} envelope : Direction -> Collage msg -> Float envelope dir col = - let - { toTop, toBottom, toLeft, toRight } = distances col - in - case dir of - Up -> toTop - Down -> toBottom - Right -> toRight - Left -> toLeft + let + { toTop, toBottom, toLeft, toRight } = + distances col + in + case dir of + Up -> + toTop + + Down -> + toBottom + + Right -> + toRight + + Left -> + toLeft @@ -198,22 +213,22 @@ envelope dir col = {-| Type alias collecting envelope distances in all four directions. -} type alias Distances = - { toTop : Float - , toBottom : Float - , toRight : Float - , toLeft : Float - } + { toTop : Float + , toBottom : Float + , toRight : Float + , toLeft : Float + } {-| Unpack a distances record in a list of points representing the corners of the envelope. -} unpack : Distances -> List Point unpack { toTop, toBottom, toRight, toLeft } = - [ ( -toLeft, -toBottom ) - , ( toRight, -toBottom ) - , ( toRight, toTop ) - , ( -toLeft, toTop ) - ] + [ ( -toLeft, -toBottom ) + , ( toRight, -toBottom ) + , ( toRight, toTop ) + , ( -toLeft, toTop ) + ] {-| Calculate the envelope in all four directions at once. @@ -227,101 +242,121 @@ Use this function if you need envelopes in multiple directions at the same time. -} distances : Collage msg -> Distances distances col = - let - points = handleBasic col.basic - ( xs, ys ) = - points - |> List.map (Core.apply col) - |> List.unzip - in - --FIXME: maybe not very efficent to do this here? - { toTop = List.maximum ys |> withDefault 0 - , toBottom = -(List.minimum ys |> withDefault 0) - , toRight = List.maximum xs |> withDefault 0 - , toLeft = -(List.minimum xs |> withDefault 0) - } + let + points = + handleBasic col.basic + + ( xs, ys ) = + points + |> List.map (Core.apply col) + |> List.unzip + in + --FIXME: maybe not very efficent to do this here? + { toTop = List.maximum ys |> withDefault 0 + , toBottom = -(List.minimum ys |> withDefault 0) + , toRight = List.maximum xs |> withDefault 0 + , toLeft = -(List.minimum xs |> withDefault 0) + } handleBasic : BasicCollage msg -> List Point handleBasic basic = - case basic of - -- Shapes -- - Core.Shape ( _, { thickness } ) (Core.Circle r) -> - let - d = 2 * r - in - handleBox thickness ( d, d ) - Core.Shape ( _, { thickness } ) (Core.Ellipse rx ry) -> - handleBox thickness ( 2 * rx, 2 * ry ) - Core.Shape ( _, { thickness } ) (Core.Rectangle w h _) -> - handleBox thickness ( w, h ) - Core.Shape ( _, { thickness } ) (Core.Polygon ps) -> - handlePoints thickness ps - Core.Shape ( _, line ) (Core.Loop path) -> - --NOTE: Use the same calculations as for paths - handleBasic (Core.Path line path) - -- Paths -- - Core.Path { thickness, cap } (Core.Polyline ps) -> - handlePoints - (if cap == Flat then - 0 - else - thickness - ) - ps - -- Boxes -- - Core.Text dims _ -> - handleBox 0 dims - Core.Image dims _ -> - handleBox 0 dims - Core.Html dims _ _ -> - handleBox 0 dims - -- Groups -- - Core.Group cols -> - cols - |> List.map (distances >> unpack) - |> List.concat - |> handlePoints 0 - Core.Subcollage _ back -> - --NOTE: We ignore the foreground and only calculate the distances of the background - --NOTE: We have to handle the rotation, this is done by `distances` - distances back - |> unpack - |> handlePoints 0 + case basic of + -- Shapes -- + Core.Shape ( _, { thickness } ) (Core.Circle r) -> + let + d = + 2 * r + in + handleBox thickness ( d, d ) + + Core.Shape ( _, { thickness } ) (Core.Ellipse rx ry) -> + handleBox thickness ( 2 * rx, 2 * ry ) + + Core.Shape ( _, { thickness } ) (Core.Rectangle w h _) -> + handleBox thickness ( w, h ) + + Core.Shape ( _, { thickness } ) (Core.Polygon ps) -> + handlePoints thickness ps + + Core.Shape ( _, line ) (Core.Loop path) -> + --NOTE: Use the same calculations as for paths + handleBasic (Core.Path line path) + + -- Paths -- + Core.Path { thickness, cap } (Core.Polyline ps) -> + handlePoints + (if cap == Flat then + 0 + + else + thickness + ) + ps + + -- Boxes -- + Core.Text dims _ -> + handleBox 0 dims + + Core.Image dims _ -> + handleBox 0 dims + + Core.Html dims _ _ -> + handleBox 0 dims + + -- Groups -- + Core.Group cols -> + cols + |> List.map (distances >> unpack) + |> List.concat + |> handlePoints 0 + + Core.Subcollage _ back -> + --NOTE: We ignore the foreground and only calculate the distances of the background + --NOTE: We have to handle the rotation, this is done by `distances` + distances back + |> unpack + |> handlePoints 0 handlePoints : Float -> List Point -> List Point handlePoints thickness = - let - thicken ( x, y ) = - let - t = thickness / 2 - in - ( if x < 0 then - x - t - else - x + t - , if y < 0 then - y - t - else - y + t - ) - in - List.map thicken + let + thicken ( x, y ) = + let + t = + thickness / 2 + in + ( if x < 0 then + x - t + + else + x + t + , if y < 0 then + y - t + + else + y + t + ) + in + List.map thicken handleBox : Float -> ( Float, Float ) -> List Point handleBox thickness ( w, h ) = - let - x = w / 2 - y = h / 2 - in - handlePoints thickness - [ ( -x, -y ) - , ( x, -y ) - , ( x, y ) - , ( -x, y ) - ] + let + x = + w / 2 + + y = + h / 2 + in + handlePoints thickness + [ ( -x, -y ) + , ( x, -y ) + , ( x, y ) + , ( -x, y ) + ] @@ -337,10 +372,11 @@ The width is equivalent to the envelopes in the left and right directions: -} width : Collage msg -> Float width col = - let - { toLeft, toRight } = distances col - in - toLeft + toRight + let + { toLeft, toRight } = + distances col + in + toLeft + toRight {-| Calculates the height of a collage. @@ -352,10 +388,11 @@ The height is equivalent to the envelopes in the up and down directions: -} height : Collage msg -> Float height col = - let - { toTop, toBottom } = distances col - in - toTop + toBottom + let + { toTop, toBottom } = + distances col + in + toTop + toBottom @@ -378,7 +415,7 @@ This is useful for getting your spacing right and for making borders. -} spacer : Float -> Float -> Collage msg spacer w h = - rectangle w h |> styled ( transparent, invisible ) + rectangle w h |> styled ( transparent, invisible ) {-| A collage that takes up no space. Good for things that appear conditionally: @@ -393,7 +430,8 @@ spacer w h = -} empty : Collage msg -empty = spacer 0 0 +empty = + spacer 0 0 @@ -410,16 +448,25 @@ Use this to position a collage next to another collage without actually composin -} place : Direction -> Collage msg -> Collage msg -> Collage msg place dir a b = - let - len = envelope dir a + envelope (facing dir) b - move = - case dir of - Up -> ( 0, len ) - Down -> ( 0, -len ) - Right -> ( len, 0 ) - Left -> ( -len, 0 ) - in - shift move b + let + len = + envelope dir a + envelope (facing dir) b + + move = + case dir of + Up -> + ( 0, len ) + + Down -> + ( 0, -len ) + + Right -> + ( len, 0 ) + + Left -> + ( -len, 0 ) + in + shift move b {-| Place a collage _beside_ another one in the given direction and combine them into a new one. @@ -435,7 +482,7 @@ The new origin will be the origin of the first argument. -} beside : Direction -> Collage msg -> Collage msg -> Collage msg beside dir a b = - stack [ a, place dir a b ] + stack [ a, place dir a b ] @@ -458,7 +505,8 @@ The new origin will be the origin of the first element in the list. -} horizontal : List (Collage msg) -> Collage msg -horizontal = List.foldr (beside Right) empty +horizontal = + List.foldr (beside Right) empty {-| Place a list of collages next to each other, @@ -481,7 +529,8 @@ The new origin will be the origin of the first element in the list. -} vertical : List (Collage msg) -> Collage msg -vertical = List.foldr (beside Down) empty +vertical = + List.foldr (beside Down) empty {-| Place a list of collages on top of each other, with their origin points stacked on the "out of page" axis. @@ -504,14 +553,15 @@ The new origin will be the origin of the first element in the list. - Note: when we create an operator `(<>)` like (<>) a b = - stack [ a, b ] + stack [ a, b ] then `(<>)` forms a monoid together with `empty`. `(<>)` is called `atop` in Diagrams. -} stack : List (Collage msg) -> Collage msg -stack = Collage.group +stack = + Collage.group {-| Impose a collage on a background. @@ -537,7 +587,7 @@ The new origin will be the origin of the background. -} impose : Collage msg -> Collage msg -> Collage msg impose front back = - Core.collage <| Core.Subcollage front back + Core.collage <| Core.Subcollage front back @@ -563,7 +613,7 @@ Anchors are created by the functions from the section below. -} align : Anchor msg -> Collage msg -> Collage msg align anchor col = - shift (Collage.opposite <| anchor col) col + shift (Collage.opposite <| anchor col) col {-| Stack a collage on top of a specified anchor of a host. @@ -581,24 +631,24 @@ Makes placing objects on a collage a lot easier: instead of: stack - [ dot - , align upperRight <| - stack - [ dot - , align bottom collage - ] - ] + [ dot + , align upperRight <| + stack + [ dot + , align bottom collage + ] + ] This does not change the origin of `collage`. -} at : Anchor msg -> Collage msg -> Collage msg -> Collage msg at anchor fore back = - stack - [ fore - |> shift (anchor back) - , back - ] + stack + [ fore + |> shift (anchor back) + , back + ] {-| Shift a collage such that the envelope in all directions is equal. @@ -609,7 +659,8 @@ This is the same as aligning on the base anchor: -} center : Collage msg -> Collage msg -center = align base +center = + align base @@ -619,7 +670,7 @@ center = align base {-| Anchors are functions which calculate a point relative to the origin of a given collage. -} type alias Anchor msg = - Collage msg -> Point + Collage msg -> Point {-| @@ -631,10 +682,11 @@ type alias Anchor msg = -} top : Anchor msg top col = - let - { toTop } = distances col - in - ( 0, toTop ) + let + { toTop } = + distances col + in + ( 0, toTop ) {-| @@ -646,10 +698,11 @@ top col = -} topRight : Anchor msg topRight col = - let - { toRight, toTop } = distances col - in - ( toRight, toTop ) + let + { toRight, toTop } = + distances col + in + ( toRight, toTop ) {-| @@ -661,10 +714,11 @@ topRight col = -} right : Anchor msg right col = - let - { toRight } = distances col - in - ( toRight, 0 ) + let + { toRight } = + distances col + in + ( toRight, 0 ) {-| @@ -676,10 +730,11 @@ right col = -} bottomRight : Anchor msg bottomRight col = - let - { toRight, toBottom } = distances col - in - ( toRight, -toBottom ) + let + { toRight, toBottom } = + distances col + in + ( toRight, -toBottom ) {-| @@ -691,10 +746,11 @@ bottomRight col = -} bottom : Anchor msg bottom col = - let - { toBottom } = distances col - in - ( 0, -toBottom ) + let + { toBottom } = + distances col + in + ( 0, -toBottom ) {-| @@ -706,10 +762,11 @@ bottom col = -} bottomLeft : Anchor msg bottomLeft col = - let - { toLeft, toBottom } = distances col - in - ( -toLeft, -toBottom ) + let + { toLeft, toBottom } = + distances col + in + ( -toLeft, -toBottom ) {-| @@ -721,10 +778,11 @@ bottomLeft col = -} left : Anchor msg left col = - let - { toLeft } = distances col - in - ( -toLeft, 0 ) + let + { toLeft } = + distances col + in + ( -toLeft, 0 ) {-| @@ -736,10 +794,11 @@ left col = -} topLeft : Anchor msg topLeft col = - let - { toLeft, toTop } = distances col - in - ( -toLeft, toTop ) + let + { toLeft, toTop } = + distances col + in + ( -toLeft, toTop ) {-| @@ -751,12 +810,17 @@ topLeft col = -} base : Anchor msg base col = - let - { toTop, toBottom, toLeft, toRight } = distances col - tx = (toRight - toLeft) / 2 - ty = (toTop - toBottom) / 2 - in - ( tx, ty ) + let + { toTop, toBottom, toLeft, toRight } = + distances col + + tx = + (toRight - toLeft) / 2 + + ty = + (toTop - toBottom) / 2 + in + ( tx, ty ) @@ -767,7 +831,7 @@ base col = -} name : String -> Collage msg -> Collage msg name string col = - { col | name = Just string } + { col | name = Just string } {-| Locate a named part of a collage and calculate the coordinates using the given anchor in the new coordinate system. @@ -784,25 +848,33 @@ we display a message on the console for your convenience. -} locate : String -> Anchor msg -> Collage msg -> Maybe Point locate string anchor this = - let - recurse col = - let - match = Maybe.map ((==) string) col.name |> withDefault False - firstOf = - --NOTE: This saves us recursing down when we found what we're looking for! - --FIXME: This is depth first!!! - Helpers.foldrLazy (Helpers.orLazy << recurse) Nothing - in - if match then - Just <| anchor col - else - Maybe.map (Core.apply col) <| - case col.basic of - Core.Group cols -> firstOf cols - Core.Subcollage fore back -> firstOf [ fore, back ] - _ -> Nothing - in - recurse this + let + recurse col = + let + match = + Maybe.map ((==) string) col.name |> withDefault False + + firstOf = + --NOTE: This saves us recursing down when we found what we're looking for! + --FIXME: This is depth first!!! + Helpers.foldrLazy (Helpers.orLazy << recurse) Nothing + in + if match then + Just <| anchor col + + else + Maybe.map (Core.apply col) <| + case col.basic of + Core.Group cols -> + firstOf cols + + Core.Subcollage fore back -> + firstOf [ fore, back ] + + _ -> + Nothing + in + recurse this {-| Breadth-first search on collages @@ -816,45 +888,55 @@ and after that going deeper down, descending into subcollages. -} locate_ : String -> Anchor msg -> Collage msg -> Maybe Point locate_ string anchor this = - let - recurse queue = - case queue of - [] -> - Nothing - col :: rest -> - let - match = Maybe.map ((==) string) col.name |> withDefault False - update = List.map (Core.combine col) - in - if match then - --NOTE: We found it! - Just <| anchor col - else - --NOTE: We go on with our search and keep track of the transformations - case col.basic of - Core.Group cols -> - --NOTE: First recurse on the rest of the queue, then go for the group contents - recurse (rest ++ update cols) - Core.Subcollage fore back -> - recurse (rest ++ update [ fore, back ]) - _ -> - recurse rest - in - recurse [ this ] + let + recurse queue = + case queue of + [] -> + Nothing + + col :: rest -> + let + match = + Maybe.map ((==) string) col.name |> withDefault False + + update = + List.map (Core.combine col) + in + if match then + --NOTE: We found it! + Just <| anchor col + + else + --NOTE: We go on with our search and keep track of the transformations + case col.basic of + Core.Group cols -> + --NOTE: First recurse on the rest of the queue, then go for the group contents + recurse (rest ++ update cols) + + Core.Subcollage fore back -> + recurse (rest ++ update [ fore, back ]) + + _ -> + recurse rest + in + recurse [ this ] {-| Return a dictionary with all named parts of given collage. -} names : Collage msg -> Dict String (Collage msg) names = - let - recurse col res = - case col.name of - Just n -> Dict.insert n col res - Nothing -> res - in - --NOTE: We use `foldr` here so named collages "higher up" will overwrite those down in the hierarchy. - Core.foldr recurse Dict.empty + let + recurse col res = + case col.name of + Just n -> + Dict.insert n col res + + Nothing -> + res + in + --NOTE: We use `foldr` here so named collages "higher up" will overwrite those down in the hierarchy. + Core.foldr recurse Dict.empty {-| Connect a list of points which are located inside a collage. @@ -865,13 +947,13 @@ the result will be _ignored_. -} connect : List ( String, Anchor msg ) -> LineStyle -> Collage msg -> Collage msg connect locations line col = - let - positions = - locations - |> List.map (\( n, a ) -> locate n a col) - |> Helpers.values - in - impose (path positions |> traced line) col + let + positions = + locations + |> List.map (\( n, a ) -> locate n a col) + |> Helpers.values + in + impose (path positions |> traced line) col @@ -882,30 +964,31 @@ connect locations line col = -} showOrigin : Collage msg -> Collage msg showOrigin col = - let - origin = - circle 3 - |> filled (uniform Color.red) - |> name "_origin_" - in - impose origin col + let + origin = + circle 3 + |> filled (uniform Color.red) + |> name "_origin_" + in + impose origin col {-| Draw a red dotted box around the collage representing the envelope. -} showEnvelope : Collage msg -> Collage msg showEnvelope col = - let - outline = - rectangle (width col) (height col) - |> outlined (dot 2 (uniform Color.red)) - |> shift (base col) - |> name "_envelope_" - in - impose outline col + let + outline = + rectangle (width col) (height col) + |> outlined (dot 2 (uniform Color.red)) + |> shift (base col) + |> name "_envelope_" + in + impose outline col {-| Show both the envelope and the origin of a collage. -} debug : Collage msg -> Collage msg -debug = showEnvelope >> showOrigin +debug = + showEnvelope >> showOrigin diff --git a/src/Collage/Render.elm b/src/Collage/Render.elm index d26c79b..28853e9 100644 --- a/src/Collage/Render.elm +++ b/src/Collage/Render.elm @@ -33,8 +33,8 @@ Please open an issue if you want to keep this._ -} svgBox : ( Float, Float ) -> Collage msg -> Html msg svgBox ( width, height ) collage = - svgAbsolute ( width, height ) <| - Collage.shift ( width / 2, -height / 2 ) collage + svgAbsolute ( width, height ) <| + Collage.shift ( width / 2, -height / 2 ) collage {-| Take a collage and render it to Html using Svg. @@ -44,8 +44,8 @@ It uses the automatically calculated envelope from the Collage.Layout module as -} svg : Collage msg -> Html msg svg collage = - svgAbsolute ( Layout.width collage, Layout.height collage ) <| - Layout.align Layout.topLeft collage + svgAbsolute ( Layout.width collage, Layout.height collage ) <| + Layout.align Layout.topLeft collage {-| Take a collage and render it to Html using Svg @@ -53,280 +53,361 @@ explicitly specifying the HTML attributes of the element. -} svgExplicit : List (Attribute msg) -> Collage msg -> Html msg svgExplicit attributes collage = - Svg.svg attributes [ render collage ] + Svg.svg attributes [ render collage ] svgAbsolute : ( Float, Float ) -> Collage msg -> Html msg svgAbsolute ( width, height ) collage = - let - w = fromFloat width - h = fromFloat height - in - Html.div - [] - [ Svg.svg - [ Svg.width w - , Svg.height h - , Svg.version "1.1" + let + w = + fromFloat width + + h = + fromFloat height + in + Html.div + [] + [ Svg.svg + [ Svg.width w + , Svg.height h + , Svg.version "1.1" + ] + [ render collage ] ] - [ render collage ] - ] render : Collage msg -> Svg msg render collage = - let - name = collage.name |> withDefault "_unnamed_" - in - case collage.basic of - Core.Path style path -> - case path of - Core.Polyline ps -> - Svg.polyline - ([ Svg.id name - , Svg.points <| decodePoints ps - ] - ++ attrs collage - ++ events collage.handlers - ) - [] - Core.Shape ( fill, line ) shape -> - case shape of - Core.Polygon ps -> - Svg.polygon - ([ Svg.id name - , Svg.points <| decodePoints ps - ] - ++ attrs collage - ++ events collage.handlers - ) - [] - Core.Circle r -> - Svg.circle - ([ Svg.id name - , Svg.r <| fromFloat r - ] - ++ attrs collage - ++ events collage.handlers - ) - [] - Core.Ellipse rx ry -> - Svg.ellipse - ([ Svg.id name - , Svg.rx <| fromFloat rx - , Svg.ry <| fromFloat ry - ] - ++ attrs collage - ++ events collage.handlers - ) - [] - Core.Rectangle w h r -> - Svg.rect - ([ Svg.id name - , Svg.rx <| fromFloat r - , Svg.ry <| fromFloat r - ] - ++ box w h - ++ attrs collage - ++ events collage.handlers - ) - [] - Core.Loop path -> - --NOTE: Use the same rendering as for a path - render { collage | basic = Core.Path line path } - Core.Text _ (Core.Chunk style str) -> - Svg.text_ - ([ Svg.id name ] - ++ attrs collage - ++ events collage.handlers - ) - [ Svg.text str ] - Core.Image ( w, h ) url -> - Svg.image - ([ Svg.id name - , Svg.xlinkHref url - ] - ++ box w h - ++ attrs collage - ++ events collage.handlers - ) - [] - Core.Html ( w, h ) extraAttrs html -> - Svg.foreignObject - ([ Svg.id name ] - ++ box w h - ++ attrs collage - ++ events collage.handlers - ++ extraAttrs - ) - [ html ] - Core.Group collages -> - --NOTE: Order of collages is reversed here! Svg renders group elements from back to front. - Svg.g (Svg.id name :: attrs collage ++ events collage.handlers) <| - List.foldl (\col res -> render col :: res) [] collages - Core.Subcollage fore back -> - --NOTE: Rendering a subcollage is the same as rendering a group, only layout calculations in `Collage.Layout` differ. - render { collage | basic = Core.Group [ fore, back ] } + let + name = + collage.name |> withDefault "_unnamed_" + in + case collage.basic of + Core.Path style path -> + case path of + Core.Polyline ps -> + Svg.polyline + ([ Svg.id name + , Svg.points <| decodePoints ps + ] + ++ attrs collage + ++ events collage.handlers + ) + [] + + Core.Shape ( fill, line ) shape -> + case shape of + Core.Polygon ps -> + Svg.polygon + ([ Svg.id name + , Svg.points <| decodePoints ps + ] + ++ attrs collage + ++ events collage.handlers + ) + [] + + Core.Circle r -> + Svg.circle + ([ Svg.id name + , Svg.r <| fromFloat r + ] + ++ attrs collage + ++ events collage.handlers + ) + [] + + Core.Ellipse rx ry -> + Svg.ellipse + ([ Svg.id name + , Svg.rx <| fromFloat rx + , Svg.ry <| fromFloat ry + ] + ++ attrs collage + ++ events collage.handlers + ) + [] + + Core.Rectangle w h r -> + Svg.rect + ([ Svg.id name + , Svg.rx <| fromFloat r + , Svg.ry <| fromFloat r + ] + ++ box w h + ++ attrs collage + ++ events collage.handlers + ) + [] + + Core.Loop path -> + --NOTE: Use the same rendering as for a path + render { collage | basic = Core.Path line path } + + Core.Text _ (Core.Chunk style str) -> + Svg.text_ + ([ Svg.id name ] + ++ attrs collage + ++ events collage.handlers + ) + [ Svg.text str ] + + Core.Image ( w, h ) url -> + Svg.image + ([ Svg.id name + , Svg.xlinkHref url + ] + ++ box w h + ++ attrs collage + ++ events collage.handlers + ) + [] + + Core.Html ( w, h ) extraAttrs html -> + Svg.foreignObject + ([ Svg.id name ] + ++ box w h + ++ attrs collage + ++ events collage.handlers + ++ extraAttrs + ) + [ html ] + + Core.Group collages -> + --NOTE: Order of collages is reversed here! Svg renders group elements from back to front. + Svg.g (Svg.id name :: attrs collage ++ events collage.handlers) <| + List.foldl (\col res -> render col :: res) [] collages + + Core.Subcollage fore back -> + --NOTE: Rendering a subcollage is the same as rendering a group, only layout calculations in `Collage.Layout` differ. + render { collage | basic = Core.Group [ fore, back ] } box : Float -> Float -> List (Attribute msg) box w h = - [ Svg.width <| fromFloat w - , Svg.height <| fromFloat h - , Svg.x <| fromFloat (-w / 2) - , Svg.y <| fromFloat (-h / 2) - ] + [ Svg.width <| fromFloat w + , Svg.height <| fromFloat h + , Svg.x <| fromFloat (-w / 2) + , Svg.y <| fromFloat (-h / 2) + ] events : List ( String, Json.Decoder msg ) -> List (Attribute msg) events handlers = - List.map (uncurry Svg.on) handlers + List.map (uncurry Svg.on) handlers attrs : Collage msg -> List (Attribute msg) attrs collage = - case collage.basic of - Core.Path line _ -> - [ Svg.stroke <| decodeFill line.fill - , Svg.strokeOpacity <| decodeFillOpacity line.fill - , Svg.strokeWidth <| fromFloat line.thickness - , Svg.strokeLinecap <| decodeCap line.cap - , Svg.strokeLinejoin <| decodeJoin line.join - , Svg.fill <| "none" - , Svg.opacity <| fromFloat collage.opacity - , Svg.transform <| decodeTransform collage - , Svg.strokeDashoffset <| fromInt line.dashPhase - , Svg.strokeDasharray <| decodeDashing line.dashPattern - ] - Core.Shape ( fill, line ) _ -> - [ Svg.fill <| decodeFill fill - , Svg.fillOpacity <| decodeFillOpacity fill - , Svg.stroke <| decodeFill line.fill - , Svg.strokeOpacity <| decodeFillOpacity line.fill - , Svg.strokeWidth <| fromFloat line.thickness - , Svg.strokeLinecap <| decodeCap line.cap - , Svg.strokeLinejoin <| decodeJoin line.join - , Svg.opacity <| fromFloat collage.opacity - , Svg.transform <| decodeTransform collage - , Svg.strokeDashoffset <| fromInt line.dashPhase - , Svg.strokeDasharray <| decodeDashing line.dashPattern - ] - Core.Text _ (Core.Chunk style str) -> - [ Svg.fill <| decodeFill (Core.Uniform style.color) - , Svg.fontFamily <| - case style.typeface of - Text.Serif -> "serif" - Text.Sansserif -> "sans-serif" - Text.Monospace -> "monospace" - Text.Font name -> name - , Svg.fontSize <| fromInt style.size - , Svg.fontWeight <| - case style.weight of - Text.Thin -> "200" - Text.Light -> "300" - Text.Regular -> "normal" - Text.Medium -> "500" - Text.SemiBold -> "600" - Text.Bold -> "bold" - Text.Black -> "800" - , Svg.fontStyle <| - case style.shape of - Text.Upright -> "normal" - Text.SmallCaps -> "normal" - Text.Slanted -> "oblique" - Text.Italic -> "italic" - , Svg.fontVariant <| - case style.shape of - Text.SmallCaps -> "small-caps" - _ -> "normal" - , Svg.textDecoration <| - case style.line of - Text.None -> "none" - Text.Under -> "underline" - Text.Over -> "overline" - Text.Through -> "line-through" - , Svg.textAnchor <| "middle" - , Svg.dominantBaseline "middle" - , Svg.opacity <| fromFloat collage.opacity - , Svg.transform <| decodeTransform collage - ] - _ -> - [ Svg.opacity <| fromFloat collage.opacity - , Svg.transform <| decodeTransform collage - ] + case collage.basic of + Core.Path line _ -> + [ Svg.stroke <| decodeFill line.fill + , Svg.strokeOpacity <| decodeFillOpacity line.fill + , Svg.strokeWidth <| fromFloat line.thickness + , Svg.strokeLinecap <| decodeCap line.cap + , Svg.strokeLinejoin <| decodeJoin line.join + , Svg.fill <| "none" + , Svg.opacity <| fromFloat collage.opacity + , Svg.transform <| decodeTransform collage + , Svg.strokeDashoffset <| fromInt line.dashPhase + , Svg.strokeDasharray <| decodeDashing line.dashPattern + ] + + Core.Shape ( fill, line ) _ -> + [ Svg.fill <| decodeFill fill + , Svg.fillOpacity <| decodeFillOpacity fill + , Svg.stroke <| decodeFill line.fill + , Svg.strokeOpacity <| decodeFillOpacity line.fill + , Svg.strokeWidth <| fromFloat line.thickness + , Svg.strokeLinecap <| decodeCap line.cap + , Svg.strokeLinejoin <| decodeJoin line.join + , Svg.opacity <| fromFloat collage.opacity + , Svg.transform <| decodeTransform collage + , Svg.strokeDashoffset <| fromInt line.dashPhase + , Svg.strokeDasharray <| decodeDashing line.dashPattern + ] + + Core.Text _ (Core.Chunk style str) -> + [ Svg.fill <| decodeFill (Core.Uniform style.color) + , Svg.fontFamily <| + case style.typeface of + Text.Serif -> + "serif" + + Text.Sansserif -> + "sans-serif" + + Text.Monospace -> + "monospace" + + Text.Font name -> + name + , Svg.fontSize <| fromInt style.size + , Svg.fontWeight <| + case style.weight of + Text.Thin -> + "200" + + Text.Light -> + "300" + + Text.Regular -> + "normal" + + Text.Medium -> + "500" + + Text.SemiBold -> + "600" + + Text.Bold -> + "bold" + + Text.Black -> + "800" + , Svg.fontStyle <| + case style.shape of + Text.Upright -> + "normal" + + Text.SmallCaps -> + "normal" + + Text.Slanted -> + "oblique" + + Text.Italic -> + "italic" + , Svg.fontVariant <| + case style.shape of + Text.SmallCaps -> + "small-caps" + + _ -> + "normal" + , Svg.textDecoration <| + case style.line of + Text.None -> + "none" + + Text.Under -> + "underline" + + Text.Over -> + "overline" + + Text.Through -> + "line-through" + , Svg.textAnchor <| "middle" + , Svg.dominantBaseline "middle" + , Svg.opacity <| fromFloat collage.opacity + , Svg.transform <| decodeTransform collage + ] + + _ -> + [ Svg.opacity <| fromFloat collage.opacity + , Svg.transform <| decodeTransform collage + ] decodeCap : Collage.LineCap -> String decodeCap cap = - case cap of - Collage.Round -> "round" - Collage.Padded -> "square" - Collage.Flat -> "butt" + case cap of + Collage.Round -> + "round" + + Collage.Padded -> + "square" + + Collage.Flat -> + "butt" decodeJoin : Collage.LineJoin -> String decodeJoin join = - case join of - Collage.Smooth -> "round" - Collage.Sharp -> "miter" - Collage.Clipped -> "bevel" + case join of + Collage.Smooth -> + "round" + + Collage.Sharp -> + "miter" + + Collage.Clipped -> + "bevel" decodePoints : List Point -> String decodePoints ps = - ps |> List.map (\( x, y ) -> String.join "," [ fromFloat x, fromFloat -y ]) |> String.join " " + ps |> List.map (\( x, y ) -> String.join "," [ fromFloat x, fromFloat -y ]) |> String.join " " decodeTransform : Collage msg -> String decodeTransform collage = - let - dx = fromFloat <| Tuple.first collage.shift - dy = fromFloat <| -(Tuple.second collage.shift) - r = fromFloat <| -collage.rotation / 2 / pi * 360 - sx = fromFloat <| Tuple.first collage.scale - sy = fromFloat <| Tuple.second collage.scale - in - String.concat - [ "translate(", dx, ",", dy, ") scale(", sx, ",", sy, ") rotate(", r, ")" ] + let + dx = + fromFloat <| Tuple.first collage.shift + + dy = + fromFloat <| -(Tuple.second collage.shift) + + r = + fromFloat <| -collage.rotation / 2 / pi * 360 + + sx = + fromFloat <| Tuple.first collage.scale + + sy = + fromFloat <| Tuple.second collage.scale + in + String.concat + [ "translate(", dx, ",", dy, ") scale(", sx, ",", sy, ") rotate(", r, ")" ] decodeFill : Core.FillStyle -> String decodeFill fs = - case fs of - Core.Uniform c -> decodeColor c - Core.Transparent -> "none" + case fs of + Core.Uniform c -> + decodeColor c + + Core.Transparent -> + "none" decodeFillOpacity : Core.FillStyle -> String decodeFillOpacity fs = - case fs of - Core.Uniform c -> decodeOpacity c - Core.Transparent -> "0" + case fs of + Core.Uniform c -> + decodeOpacity c + + Core.Transparent -> + "0" decodeColor : Color -> String decodeColor c = - let - { red, green, blue } = Color.toRgba c - in - Color.rgb red green blue - |> Color.toCssString + let + { red, green, blue } = + Color.toRgba c + in + Color.rgb red green blue + |> Color.toCssString decodeOpacity : Color -> String decodeOpacity c = - let - { alpha } = Color.toRgba c - in - fromFloat alpha + let + { alpha } = + Color.toRgba c + in + fromFloat alpha decodeDashing : List ( Int, Int ) -> String decodeDashing ds = - let - decodeOnOff ( x, y ) = - String.join "," [ fromInt x, fromInt y ] - in - ds - |> List.map decodeOnOff - |> String.join " " + let + decodeOnOff ( x, y ) = + String.join "," [ fromInt x, fromInt y ] + in + ds + |> List.map decodeOnOff + |> String.join " " diff --git a/src/Collage/Super.elm b/src/Collage/Super.elm index 62ff7d3..99fb553 100644 --- a/src/Collage/Super.elm +++ b/src/Collage/Super.elm @@ -9,4 +9,4 @@ import Collage.Text as Text Only for internal usage. -} type alias BasicCollage msg = - Core.BasicCollage Core.FillStyle Collage.LineStyle Text.Style msg + Core.BasicCollage Core.FillStyle Collage.LineStyle Text.Style msg diff --git a/src/Collage/Text.elm b/src/Collage/Text.elm index e8439f7..bbbcb03 100644 --- a/src/Collage/Text.elm +++ b/src/Collage/Text.elm @@ -1,14 +1,14 @@ module Collage.Text exposing - ( Text - , fromString, empty - , Typeface(..), typeface, color - , size, tiny, small, normal, large, huge, enormous - , Shape(..), shape, Weight(..), weight - , Line(..), line - , Style, style, defaultStyle - , width, height - {- (.) -} - ) + ( Text + , fromString, empty + , Typeface(..), typeface, color + , size, tiny, small, normal, large, huge, enormous + , Shape(..), shape, Weight(..), weight + , Line(..), line + , Style, style, defaultStyle + , width, height + {- (.) -} + ) {-| A library for styling and displaying text. @@ -116,7 +116,7 @@ import Color exposing (Color) {-| Opaque type representing styled text. -} type alias Text = - Core.Text Style + Core.Text Style @@ -128,23 +128,26 @@ type alias Text = To show the string "Hello World!" on screen in large, dark red, italics, you could say: fromString "Hello World!" - |> size large - |> color Color.darkRed - |> shape Italic - |> Collage.rendered + |> size large + |> color Color.darkRed + |> shape Italic + |> Collage.rendered -} fromString : String -> Text -fromString = Core.Chunk defaultStyle +fromString = + Core.Chunk defaultStyle {-| Text with nothing in it. - empty = fromString "" + empty = + fromString "" -} empty : Text -empty = fromString "" +empty = + fromString "" @@ -154,13 +157,13 @@ empty = fromString "" {-| Specifies the styling (color, typeface, weight, etc.) of text. -} type alias Style = - { typeface : Typeface - , size : Int - , color : Color - , shape : Shape - , weight : Weight - , line : Line - } + { typeface : Typeface + , size : Int + , color : Color + , shape : Shape + , weight : Weight + , line : Line + } {-| Give some text a predefined style. @@ -183,7 +186,7 @@ you could apply it to text like this: -} style : Style -> Text -> Text style newstyle (Core.Chunk _ str) = - Core.Chunk newstyle str + Core.Chunk newstyle str {-| Plain black text. @@ -192,24 +195,24 @@ It uses the browsers default typeface and text height. No decorations are used. defaultStyle = - { typeface = Sansserif - , size = normal - , color = Color.black - , shape = Upright - , weight = Regular - , line = None - } + { typeface = Sansserif + , size = normal + , color = Color.black + , shape = Upright + , weight = Regular + , line = None + } -} defaultStyle : Style defaultStyle = - { typeface = Sansserif - , size = normal - , color = Color.black - , shape = Upright - , weight = Regular - , line = None - } + { typeface = Sansserif + , size = normal + , color = Color.black + , shape = Upright + , weight = Regular + , line = None + } @@ -223,21 +226,21 @@ Use `Font` to specify a concrete typeface. -} type Typeface - = Serif - | Sansserif - | Monospace - | Font String + = Serif + | Sansserif + | Monospace + | Font String {-| Set the typeface of some text. fromString "Text in my favorite font" - |> typeface (Font "Lato") + |> typeface (Font "Lato") -} typeface : Typeface -> Text -> Text typeface newface (Core.Chunk sty str) = - Core.Chunk { sty | typeface = newface } str + Core.Chunk { sty | typeface = newface } str {-| Set the color of some text. @@ -245,12 +248,12 @@ typeface newface (Core.Chunk sty str) = Use the Color module to specify colors. fromString "Nice blue text" - |> color Color.blue + |> color Color.blue -} color : Color -> Text -> Text color newcolor (Core.Chunk sty str) = - Core.Chunk { sty | color = newcolor } str + Core.Chunk { sty | color = newcolor } str @@ -260,48 +263,54 @@ color newcolor (Core.Chunk sty str) = {-| Set the size of some text. fromString "Big text" - |> size huge + |> size huge -} size : Int -> Text -> Text size newsize (Core.Chunk sty str) = - Core.Chunk { sty | size = newsize } str + Core.Chunk { sty | size = newsize } str {-| 11 px -} tiny : Int -tiny = 11 +tiny = + 11 {-| 13 px -} small : Int -small = 13 +small = + 13 {-| 16 px -} normal : Int -normal = 16 +normal = + 16 {-| 19 px -} large : Int -large = 19 +large = + 19 {-| 23 px -} huge : Int -huge = 23 +huge = + 23 {-| 27 px -} enormous : Int -enormous = 27 +enormous = + 27 @@ -311,50 +320,50 @@ enormous = 27 {-| Possible shapes for a piece of text. -} type Shape - = Upright - | SmallCaps - | Slanted - | Italic + = Upright + | SmallCaps + | Slanted + | Italic {-| Set the shape of some text. fromString "Italic text" - |> shape Italic + |> shape Italic -} shape : Shape -> Text -> Text shape newshape (Core.Chunk sty str) = - Core.Chunk { sty | shape = newshape } str + Core.Chunk { sty | shape = newshape } str {-| Possible weights for a piece of text. -} type Weight - = Thin - | Light - | Regular - | Medium - | SemiBold - | Bold - | Black + = Thin + | Light + | Regular + | Medium + | SemiBold + | Bold + | Black {-| Set the weight of some text. fromString "Bold text" - |> weight Bold + |> weight Bold -} weight : Weight -> Text -> Text weight newweight (Core.Chunk sty str) = - Core.Chunk { sty | weight = newweight } str + Core.Chunk { sty | weight = newweight } str type Stretch - = Condensed - | Normal - | Expanded + = Condensed + | Normal + | Expanded @@ -369,10 +378,10 @@ type Stretch {-| Styles for lines on or over some text. -} type Line - = None - | Under - | Over - | Through + = None + | Under + | Over + | Through {-| Put lines on text. @@ -390,7 +399,7 @@ This allows you to add an underline, an overline, or strike out text: -} line : Line -> Text -> Text line newline (Core.Chunk sty str) = - Core.Chunk { sty | line = newline } str + Core.Chunk { sty | line = newline } str @@ -426,8 +435,8 @@ line newline (Core.Chunk sty str) = -} width : Text -> Float width ((Core.Chunk sty str) as text) = - --FIXME: Native.Text.width (toCssFontSpec sty) str - height text / 2 * toFloat (String.length str) + --FIXME: Native.Text.width (toCssFontSpec sty) str + height text / 2 * toFloat (String.length str) {-| The height of the text when displayed on the user screen. @@ -435,16 +444,16 @@ width ((Core.Chunk sty str) as text) = This is equal to the text size: fromString "Hello World!" - |> size 16 - |> height - == 16 + |> size 16 + |> height + == 16 (Now you know why newlines are a bad idea...) -} height : Text -> Float height (Core.Chunk sty _) = - toFloat sty.size + toFloat sty.size {-| Example: @@ -457,36 +466,66 @@ font: italic small-caps bolder condensed 16px/3 cursive; -} toCssFontSpec : Style -> String toCssFontSpec sty = - let - --NOTE: adding font-stretch makes spec not parse... - spec = - [ -- font-style - case sty.shape of - Upright -> "normal" - SmallCaps -> "normal" - Slanted -> "oblique" - Italic -> "italic" - , -- font-variant - case sty.shape of - SmallCaps -> "small-caps" - _ -> "normal" - , -- font-weight - case sty.weight of - Thin -> "200" - Light -> "300" - Regular -> "normal" - Medium -> "500" - SemiBold -> "600" - Bold -> "bold" - Black -> "800" - , -- font-size - String.fromInt sty.size ++ "px" - , -- font-family - case sty.typeface of - Serif -> "serif" - Sansserif -> "sans-serif" - Monospace -> "monospace" - Font name -> name - ] - in - String.concat <| List.intersperse " " <| spec + let + --NOTE: adding font-stretch makes spec not parse... + spec = + [ -- font-style + case sty.shape of + Upright -> + "normal" + + SmallCaps -> + "normal" + + Slanted -> + "oblique" + + Italic -> + "italic" + , -- font-variant + case sty.shape of + SmallCaps -> + "small-caps" + + _ -> + "normal" + , -- font-weight + case sty.weight of + Thin -> + "200" + + Light -> + "300" + + Regular -> + "normal" + + Medium -> + "500" + + SemiBold -> + "600" + + Bold -> + "bold" + + Black -> + "800" + , -- font-size + String.fromInt sty.size ++ "px" + , -- font-family + case sty.typeface of + Serif -> + "serif" + + Sansserif -> + "sans-serif" + + Monospace -> + "monospace" + + Font name -> + name + ] + in + String.concat <| List.intersperse " " <| spec diff --git a/src/Helpers.elm b/src/Helpers.elm index 09585ca..07bc939 100644 --- a/src/Helpers.elm +++ b/src/Helpers.elm @@ -1,8 +1,8 @@ module Helpers exposing - ( foldrLazy - , orLazy - , values - ) + ( foldrLazy + , orLazy + , values + ) {-| -} @@ -13,9 +13,12 @@ module Helpers exposing -} orLazy : Maybe a -> (() -> Maybe a) -> Maybe a orLazy ma fmb = - case ma of - Nothing -> fmb () - Just _ -> ma + case ma of + Nothing -> + fmb () + + Just _ -> + ma {-| Convert a list of `Maybe a` to a list of `a` only for the values different from `Nothing`. @@ -24,14 +27,18 @@ orLazy ma fmb = -} values : List (Maybe a) -> List a -values = List.foldr foldrValues [] +values = + List.foldr foldrValues [] foldrValues : Maybe a -> List a -> List a foldrValues item list = - case item of - Nothing -> list - Just v -> v :: list + case item of + Nothing -> + list + + Just v -> + v :: list @@ -40,6 +47,9 @@ foldrValues item list = foldrLazy : (e -> (() -> a) -> a) -> a -> List e -> a foldrLazy f acc list = - case list of - [] -> acc - x :: xs -> f x (\() -> foldrLazy f acc xs) + case list of + [] -> + acc + + x :: xs -> + f x (\() -> foldrLazy f acc xs)