MacCASOutreach/graphicsvg/2.0.0

NOTE: this package is not compatible with Elm 0.19.1

GraphicSVG

A library for creating SVG graphics in a way that is compatible with Elm's old Graphics library. Also includes built-in functions for creating games and other applications including keyboard presses and mouse movements.

Basic Types

type Stencil

A primitive template representing the shape you wish to draw. This must be turned into a Shape before being drawn to the screen with collage (see below).

type Shape userMsg

A filled, outlined, or filled and outlined object that can be drawn to the screen using collage.

type alias GraphicSVG userMsg =
Collage (Msg userMsg)

The GraphicSVG type alias represents the drawable surface of the window.

This type is only used to define a type signature for a user defined view as follows:

view : GraphicSVG.GraphicSVG userMsg

for use with graphicsApp and as follows:

view : Model -> GraphicSVG.GraphicSVG MyMsg

for use with notificationsApp, gameApp and cmdApp.

These assume that Model is the name of the user model type alias and MyMsg is the name of the user message type. Simply substitute the names actually used for these labels.

Rendering To Screen

collage : Float -> Float -> List (Shape userMsg) -> Collage userMsg

Creates a blank canvas on which you can draw. Takes a width, height and a list of Shapes. Use this in your view functions in the three types of Apps above:

collage 500 500 
    [ 
        circle 10 |> filled red
    ]

Graphics App

graphicsApp : JustGraphics a -> GraphicsProgram a

The simplest way to render graphics to the screen. These graphics will be static (they don't move) and cannot be interacted with. This is great for beginners or for when only need static graphics are needed. Note that your view function is bare, with no parameters:

view = collage 500 500 
    [ 
        circle 10 |> filled red
    ]

graphicsApp takes a parameter like { view = view } so the main program that would get the whole thing started for the above view would be:

main =
    graphicsApp { view = view }
type alias GraphicsProgram userMsg =
Program Never ( Int, HiddenModel () ) (Msg userMsg)

This type alias is only used as a target for a user main type signature to make the type signature more clear and concise when main calls graphicsApp:

main : GraphicsProgram userMsg
main =
    graphicsApp { view = view }

Note that userMsg can be anything as no messages are used in this type of program.

Notifications App

notificationsApp : GraphicsApp model userMsg -> NotificationsProgram model userMsg

Like graphicsApp, but you can add interactivity to your graphics by using the notify* functions. This allows you to learn Elm's architecture in a fun way with graphics. Note that your view function needs a model parameter now, which in this example is the colour of the shape:

view model =
    collage 500 500
        [ 
            circle 10 |> filled model |> notifyTap Change
        ]

notificationApp takes a parameter like:

{
  model = model
, view = view
, update = update
}

so the functions that would be required to make the above view function work are as follows:

type Msg
    = Change


update msg model =
    case msg of
        Change ->
            green


main =
    notificationsApp
        { model = red -- causes circle to start red
        , update = update -- function which changes the model
        , view = view
        }

which will cause the drawn red circle to change to green the first time it is mouse clicked or tapped.

type alias NotificationsProgram model userMsg =
Program Never ( model, HiddenModel () ) (Msg userMsg)

This type alias is only used as a target for a user main type signature to make the type signature more clear and concise when main calls notificationsApp:

main : NotificationProgram Model MyMsg
main =
    notificationsApp { model = init, update = update, view = view }

where Model is the type alias of the user persistent model, and MyMsg is the name of the user defined message type; if other names are used, they can be substituted for these names.

Game App

type alias GetKeyState =
( Keys -> KeyState, ( Float, Float ), ( Float, Float ) )

GetKeyState returns a triple where the first argument is of type Keys -> KeyState so you can ask if a certain key is pressed. The other two are tuples of arrow keys and WASD keys, respectively. They're in the form (x,y) which represents the key presses of each player. For example, (0,-1) represents the left arrow (or "A") key, and (1,1) would mean the up (or "W") and right (or "D") key are being pressed at the same time.

type Keys
= Key String
| Backspace
| Tab
| Enter
| Shift
| Ctrl
| Alt
| Caps
| LeftArrow
| UpArrow
| RightArrow
| DownArrow
| Delete
| Space

Includes all the regular keys. Ask for letters and numbers using Key String, e.g. Key "a" or Key "3".

= JustDown
| Down
| JustUp
| Up

The possible states when you ask for a key's state:

  • JustDown is the frame after the key went down (will show up exactly once per press)
  • Down is a press that is continuing for more than one frame
  • JustUp is the frame after the key went up / stopped being pressed (will show up exactly once per press)
  • Up means the key is not currently being pressed nor was it recently released
InputHandler userMsg
-> GraphicsApp model userMsg
-> GameProgram model userMsg

Automatically maps time and keyboard presses to your program. This should be all you need for making complex interactive games and animations. gameApp takes two parameters: one is your own type of InputHandler message which will be automatically called each time the browser window is refreshed (30 times per second) of the form Float -> GetKeyState -> UserMsg and the other is

{
  model = model
, view = view
, update = update
}

The following program causes animation of the drawn line, causing it to spin around; also, a press of the "r" key causes the direction of the spin to reverse:

type Msg
    = Tick Float GetKeyState


type alias Model =
    { angle : Float, speed : Float }


init =
    { angle = 0, speed = 1 }


update msg model =
    case msg of
        Tick _ ( keys, _, _ ) ->
            case keys (Key "r") of
                JustDown ->
                    { model
                        | angle = model.angle - model.speed
                        , speed = -model.speed
                    }


                _ ->
                    { model | angle = model.angle + model.speed }


view model =
    collage 500 500
        [ line ( 0, 0 ) ( 250, 0 )
            |> outlined (solid 1) green
            |> rotate (degrees model)
        ]


main =
    gameApp Tick { 
                   model = init
                 , update = update
                 , view = view 
                 }
type alias GameProgram model userMsg =
Program
Never
( model, HiddenModel (InputHandler userMsg) )
(Msg userMsg)

This type alias is only used as a target for a user main type signature to make the type signature more clear and concise when main calls gameApp:

main : GamesProgram Model Msg
main =
    gameApp Tick { 
                   model = init
                 , update = update
                 , view = view 
                 }

where Tick is the message handler called once per browser window update, Model is the type alias of the user persistent model, and Msg is the name of the user message type; if other names are used, they can be substituted for these names.

Cmd App

InputHandler userMsg
-> CmdApp model userMsg
-> CmdProgram model userMsg

Advanced Function Warning! cmdApp takes two parameters: one is your own type of the form Float -> GetKeyState -> CustomMsg and the other is

{
  init = (model, cmd)
, view = view
, update = update
, subscriptions = subscriptions
}

This matches the Elm architecture and is analogous to Html.program.

type alias CmdProgram model userMsg =
Program
Never
( model, HiddenModel (InputHandler userMsg) )
(Msg userMsg)

This type alias is only used as a target for a user main type signature to make the type signature more clear and concise when main calls cmdApp:

main : CmdProgram Model Msg
main =
    cmdApp Tick { model = init, update = update, view = view, subscriptions = subscriptions }

where Tick is a message handler called once per browser window update, Model is the type alias of the user persistent model, and Msg is the name of the user message type; if other names are used, they can just be substituted for these names.

Stencils

line : ( Float, Float ) -> ( Float, Float ) -> Stencil

Create a line from a point to a point. Use outlined to convert to a viewable Shape.

polygon : List ( Float, Float ) -> Stencil

Create a closed shape given a list of points. Can use outlined or filled to convert to a Shape.

openPolygon : List ( Float, Float ) -> Stencil

Create an open shape given a list of points. Unlike with polygon, the first and last points will not join up automatically. Can use outlined or filled to convert to a Shape.

ngon : Int -> Float -> Stencil

Create a regular polygon with a given number of sides and radius. Examples:

ngon 3 50 -- triangle
ngon 5 50 -- pentagon
ngon 8 50 -- octogon
triangle : Float -> Stencil

Synonym for ngon 3. Creates a triangle with a given size.

square : Float -> Stencil

Creates a square with a given side length. (Synonym for rect s s)

rect : Float -> Float -> Stencil

Creates a rectangle with a given width and height.

rectangle : Float -> Float -> Stencil

Synonym for rect.

roundedRect : Float -> Float -> Float -> Stencil

Creates a rectangle with a given width, height, and circular rounded corners with the given radius.

circle : Float -> Stencil

Creates a circle with a given radius.

oval : Float -> Float -> Stencil

Creates an oval with a given width and height.

wedge : Float -> Float -> Stencil

Creates a wedge with a given radius, and a given fraction of a circle.

wedge 50 0.5 -- semi-circle
wedge 50 0.25 -- quarter-circle
wedge 50 0.75 -- three-quarter circle

Creating Shapes from Stencils

filled : Color -> Stencil -> Shape userMsg

Fill a Stencil with a Color, creating a Shape.

circle 10
    |> filled red
outlined : LineType -> Color -> Stencil -> Shape userMsg

Outline a Stencil with a LineType and Color, creating a Shape.

circle 10
    |> outlined (solid 5) red
addOutline : LineType -> Color -> Shape userMsg -> Shape userMsg

Add an outline to an already-filled Shape.

circle 10
    |> filled red
    |> addOutline (solid 5) white
rgb : Float -> Float -> Float -> Color

Define a colour given its red, green and blue components.

rgba : Float -> Float -> Float -> Float -> Color

Define a colour given its red, green, blue and alpha components. Alpha is a decimal number (Float) from 0 to 1 representing the level of transparency.

hsl : Float -> Float -> Float -> Color

Define a colour given its hue, saturation and light components.

hsla : Float -> Float -> Float -> Float -> Color

Define a colour given its hue, saturation, light and alpha components. Alpha is a decimal number (Float) from 0 to 1 representing the level of transparency.

Grouping Shapes

group : List (Shape userMsg) -> Shape userMsg

Combine any number of Shape types into one Shape that can be transformed (moved, rotated, scaled, etc) as one Shape.

Curves

curve : ( Float, Float ) -> List Pull -> Stencil

Creates a curve starting at a point, pulled towards a point, ending at a third point. For example,

curve (0,0) [Pull (0,10) (0,20)]

gives a curve starting at (0,0), pulled towards (0,10) and ending at (0,20).

Think about curves as what you get when you take a bunch of bendy sticks with their ends glued down to a board, and then pulling each stick towards another point. You always need an initial point and at least one Pull, but you can add as many Pulls as you want to add additional curve segments, but each curve segment can only bend one way, since it is pulled in one direction.

type Pull
= Pull ( Float, Float ) ( Float, Float )

To make it easier to read the code defining a curve, and to make sure we always use the right number of curve points and pull points (which is one more curve point than pull points), we define a special Pull type, whose first point is the point we pull towards, and second point is the end point for this curve segments.

curveHelper : Shape userMsg -> Shape userMsg

Apply to a curve or group of curves in order to view their start points, end points and Pull points. Helpful while perfecting curves.

curve (0,0) [Pull (0,10) (0,20)]
    |> curveHelper

Line Styles

The LineType type is used to define the appearance of an outline for a Stencil. LineType also defines the appearence of line and curve.

solid : Float -> LineType

Define a solid LineType with the given width.

dotted : Float -> LineType

Define a dotted LineType with the given width.

dashed : Float -> LineType

Define a dashed LineType with the given width. Dashes are short line segments, versus dots which are theoretically points, but may be drawn with very short line segments.

longdash : Float -> LineType

Define a dashed LineType with the given width, where the dashes are longer than normal.

dotdash : Float -> LineType

Define a LineType with the given width, including alternating dots and dashes.

custom : List ( Float, Float ) -> Float -> LineType

A custom line defined by a list of (on,off):

custom [(10,5)] 5 -- a line with dashes 10 long and spaces 5 long
custom [(10,5),(20,5)] -- on for 10, off 5, on 20, off 5

Text

type Face

The Face type describes the appearance of a text Stencil.

type Font

The Font type describes the font of a text Stencil.

text : String -> Stencil

Creates a text Stencil. You can change this Stencil using the text helper functions. Note that |> filled ... or |> outlined ... must go at the end of the text helper functions (ie note that all these functions have type Stencil -> Stencil). For example,

text "Hello World"
    |> fixedwidth
    |> bold
    |> size 14
    |> filled black
size : Float -> Stencil -> Stencil

Apply to a text Stencil to change the font size of the text.

The size has a unit called "points", which depends on the size and type of screen used, but try 12 to start.

bold : Stencil -> Stencil

Apply to a text Stencil to make the text bold.

italic : Stencil -> Stencil

Apply to a text Stencil to make the text italicized (slanted).

underline : Stencil -> Stencil

Apply to a text Stencil to underline the text.

strikethrough : Stencil -> Stencil

Apply to a text Stencil to put a line through the centre of the text.

centered : Stencil -> Stencil

Apply to a text Stencil to centre the text.

sansserif : Stencil -> Stencil

Apply to a text Stencil to render the text with a Sans Serif font (ie one without thinner and thicker bits).

serif : Stencil -> Stencil

Apply to a text Stencil to render the text with a Serif font (ie one with thinner and thicker bits).

fixedwidth : Stencil -> Stencil

Apply to a text Stencil to render the text Stencil with a font in which every character has the same width. This will mean that the letters line up from line to line which is important in programming languages like Elm.

customFont : String -> Stencil -> Stencil

Apply to a text Stencil to render the text with a font of your choosing by specifying its name in a String.

Use this sparingly as support for each font will vary across browsers and devices.

Transformations

move : ( Float, Float ) -> Shape userMsg -> Shape userMsg

Move a Shape by a number of units along the x-axis and y-axis.

rotate : Float -> Shape userMsg -> Shape userMsg

Rotate a Shape by the specified amount (in radians). Use the degrees function to convert from degrees into radians:

[
    rect 30 60
        |> filled blue
        |> rotate(degrees 30)
]
scale : Float -> Shape userMsg -> Shape userMsg

Scale a Shape by a given factor.

scaleX : Float -> Shape userMsg -> Shape userMsg

Scale a Shape in the x-axis by a given factor.

scaleY : Float -> Shape userMsg -> Shape userMsg

Scale a Shape in the y-axis by a given factor.

mirrorX : Shape userMsg -> Shape userMsg

Flip a Shape along the x-axis.

mirrorY : Shape userMsg -> Shape userMsg

Flip a Shape along the y-axis.

Notifications

notifyTap : userMsg -> Shape (Msg userMsg) -> Shape (Msg userMsg)

Receive a message (userMsg) when a Shape is clicked or tapped.

(( Float, Float ) -> userMsg)
-> Shape (Msg userMsg)
-> Shape (Msg userMsg)

Receive a message (userMsg) with the x and y position of the mouse or finger when the Shape is clicked or tapped.

notifyEnter : userMsg -> Shape (Msg userMsg) -> Shape (Msg userMsg)

Receive a message (userMsg) when the mouse enters a Shape.

(( Float, Float ) -> userMsg)
-> Shape (Msg userMsg)
-> Shape (Msg userMsg)

Receive a message (userMsg) with the x and y position of the mouse when the mouse enters a Shape.

notifyLeave : userMsg -> Shape (Msg userMsg) -> Shape (Msg userMsg)

Receive a message (userMsg) when the mouse leaves a Shape.

(( Float, Float ) -> userMsg)
-> Shape (Msg userMsg)
-> Shape (Msg userMsg)

Receive a message (userMsg) with the x and y position of the mouse when the mouse leaves a Shape.

(( Float, Float ) -> userMsg)
-> Shape (Msg userMsg)
-> Shape (Msg userMsg)

Receive a message (userMsg) with the x and y position of the mouse when the mouse is moved across a Shape.

notifyMouseDown : userMsg -> Shape (Msg userMsg) -> Shape (Msg userMsg)

Receive a message (userMsg) when the mouse button is pressed while the cursor is over a Shape.

(( Float, Float ) -> userMsg)
-> Shape (Msg userMsg)
-> Shape (Msg userMsg)

Receive a message (userMsg) with the x and y position of the mouse when the mouse button is pressed while the cursor is over a Shape.

notifyMouseUp : userMsg -> Shape (Msg userMsg) -> Shape (Msg userMsg)

Receive a message (userMsg) when the mouse button is released while the cursor is over a Shape.

(( Float, Float ) -> userMsg)
-> Shape (Msg userMsg)
-> Shape (Msg userMsg)

Receive a message (userMsg) with the x and y position of the mouse when the mouse button is released while the cursor is over a Shape.

notifyTouchStart : userMsg -> Shape (Msg userMsg) -> Shape (Msg userMsg)

Receive a message (userMsg) when the user begins touching a Shape.

(( Float, Float ) -> userMsg)
-> Shape (Msg userMsg)
-> Shape (Msg userMsg)

Receive a message (userMsg) with the x and y position of the user's finger when the user begins touching a Shape.

notifyTouchEnd : userMsg -> Shape (Msg userMsg) -> Shape (Msg userMsg)

Receive a message (userMsg) when the user lifts their finger off a Shape.

(( Float, Float ) -> userMsg)
-> Shape (Msg userMsg)
-> Shape (Msg userMsg)

Receive a message (userMsg) with the x and y position of the user's finger when the user lifts their finger off a Shape.

(( Float, Float ) -> userMsg)
-> Shape (Msg userMsg)
-> Shape (Msg userMsg)

Receive a message (userMsg) with the x and y position of the user's finger when the user moves their finger over a Shape.

Miscellaneous

makeTransparent : Float -> Shape userMsg -> Shape userMsg

Make a Shape transparent by the fraction given. Multiplies on top of other transparencies:

circle 10
    |> filled red
    |> makeTransparent 0.5
--results in a transparency of 0.5 (half vislible)


circle 10
    |> filled red
    |> makeTransparent 0.5
    |> makeTransparent 0.5
--results in a transparency of 0.25 (a quarter visible)

Helpers

graphPaper : Float -> Shape userMsg

Creates a graph paper with squares of a given size.

graphPaperCustom : Float -> Float -> Color -> Shape userMsg

Creates graph paper with squares of a given size, with a user-defined thickness and colour.

map : (a -> b) -> Shape (Msg a) -> Shape (Msg b)

To compose multiple pages or components which each have a Msg/view/update, we need to map messages. (Ask if you don't know what this means.)

Let there be colours!

type Color

The Color type is used for filling or outlining a Stencil.

black : Color
blank : Color
blue : Color
brown : Color
charcoal : Color
darkBlue : Color
darkBrown : Color
darkCharcoal : Color
darkGray : Color
darkGreen : Color
darkGrey : Color
darkOrange : Color
darkPurple : Color
darkRed : Color
darkYellow : Color
gray : Color
green : Color
grey : Color
hotPink : Color
lightBlue : Color
lightBrown : Color
lightGray : Color
lightGreen : Color
lightGrey : Color
lightOrange : Color
lightPurple : Color
lightRed : Color
lightYellow : Color
orange : Color
pink : Color
purple : Color
red : Color
white : Color
yellow : Color