2013-03-06 17:24:17 +00:00
|
|
|
module Graphics.Collage where
|
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| The collage API is for freeform graphics. You can move, rotate, scale, etc.
|
|
|
|
all sorts of forms including lines, shapes, images, and elements.
|
|
|
|
|
|
|
|
# Unstructured Graphics
|
|
|
|
@docs collage
|
|
|
|
|
|
|
|
# Creating Forms
|
|
|
|
@docs toForm, filled, textured, gradient, outlined, traced
|
|
|
|
|
|
|
|
# Transforming Forms
|
|
|
|
@docs move, moveX, moveY, scale, rotate, alpha
|
|
|
|
|
|
|
|
# Grouping Forms
|
|
|
|
Grouping forms makes it easier to write modular graphics code. You can create
|
|
|
|
a form that is a composite of many subforms. From there it is easy to transform
|
|
|
|
it as a single unit.
|
|
|
|
|
|
|
|
@docs group, groupTransform
|
|
|
|
|
|
|
|
# Shapes
|
|
|
|
@docs rect, oval, square, circle, ngon, polygon
|
|
|
|
|
|
|
|
# Paths
|
|
|
|
@docs segment, path
|
|
|
|
|
|
|
|
# Line Styles
|
|
|
|
@docs solid, dashed, dotted, LineStyle, LineCap, LineJoin, defaultLine
|
|
|
|
|
|
|
|
-}
|
|
|
|
|
2013-07-27 11:06:22 +00:00
|
|
|
import open Basics
|
2013-04-29 03:57:48 +00:00
|
|
|
import List
|
2013-07-27 11:06:22 +00:00
|
|
|
import Either (Either, Left, Right)
|
2013-08-19 18:57:18 +00:00
|
|
|
import Transform2D (Transform2D, identity)
|
2013-07-29 21:23:04 +00:00
|
|
|
import Native.Graphics.Collage
|
2013-08-22 02:10:26 +00:00
|
|
|
import Graphics.Element (Element)
|
2013-07-27 11:06:22 +00:00
|
|
|
import Color (Color, black, Gradient)
|
|
|
|
import Maybe (Maybe)
|
|
|
|
import JavaScript (JSString)
|
2013-03-06 17:24:17 +00:00
|
|
|
|
2013-03-21 09:29:23 +00:00
|
|
|
type Form = {
|
|
|
|
theta : Float,
|
|
|
|
scale : Float,
|
|
|
|
x : Float,
|
|
|
|
y : Float,
|
2013-06-13 19:00:17 +00:00
|
|
|
alpha : Float,
|
2013-03-21 09:29:23 +00:00
|
|
|
form : BasicForm
|
|
|
|
}
|
2013-03-06 17:24:17 +00:00
|
|
|
|
|
|
|
data FillStyle
|
2013-03-21 09:29:23 +00:00
|
|
|
= Solid Color
|
2013-03-06 17:24:17 +00:00
|
|
|
| Texture String
|
2013-07-29 17:26:53 +00:00
|
|
|
| Grad Gradient
|
2013-03-06 17:24:17 +00:00
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| The shape of the ends of a line. -}
|
2013-05-17 20:46:08 +00:00
|
|
|
data LineCap = Flat | Round | Padded
|
2013-03-27 01:50:18 +00:00
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| The shape of the “joints” of a line, where each line segment
|
|
|
|
meets. `Sharp` takes an argument to limit the length of the joint. This
|
|
|
|
defaults to 10.
|
|
|
|
-}
|
2013-05-17 20:46:08 +00:00
|
|
|
data LineJoin = Smooth | Sharp Float | Clipped
|
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| All of the attributes of a line style. This lets you build up a line style
|
|
|
|
however you want. You can also update existing line styles with record updates.
|
|
|
|
-}
|
2013-03-27 01:50:18 +00:00
|
|
|
type LineStyle = {
|
|
|
|
color : Color,
|
|
|
|
width : Float,
|
|
|
|
cap : LineCap,
|
|
|
|
join : LineJoin,
|
|
|
|
dashing : [Int],
|
|
|
|
dashOffset : Int
|
|
|
|
}
|
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| The default line style, which is solid black with flat caps and sharp joints.
|
|
|
|
You can use record updates to build the line style you
|
|
|
|
want. For example, to make a thicker line, you could say:
|
|
|
|
|
|
|
|
{ defaultLine | width <- 10 }
|
|
|
|
-}
|
2013-05-05 19:11:45 +00:00
|
|
|
defaultLine : LineStyle
|
2013-03-27 01:50:18 +00:00
|
|
|
defaultLine = {
|
2013-07-27 11:06:22 +00:00
|
|
|
color = black,
|
2013-03-27 01:50:18 +00:00
|
|
|
width = 1,
|
2013-05-17 20:46:08 +00:00
|
|
|
cap = Flat,
|
|
|
|
join = Sharp 10,
|
2013-03-27 01:50:18 +00:00
|
|
|
dashing = [],
|
2013-05-21 08:03:51 +00:00
|
|
|
dashOffset = 0
|
2013-03-27 01:50:18 +00:00
|
|
|
}
|
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| Create a solid line style with a given color. -}
|
2013-05-05 19:11:45 +00:00
|
|
|
solid : Color -> LineStyle
|
2013-03-27 16:52:16 +00:00
|
|
|
solid clr = { defaultLine | color <- clr }
|
2013-05-17 20:46:08 +00:00
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| Create a dashed line style with a given color. Dashing equals `[8,4]`. -}
|
2013-05-05 19:11:45 +00:00
|
|
|
dashed : Color -> LineStyle
|
2013-03-27 16:52:16 +00:00
|
|
|
dashed clr = { defaultLine | color <- clr, dashing <- [8,4] }
|
2013-05-17 20:46:08 +00:00
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| Create a dotted line style with a given color. Dashing equals `[3,3]`. -}
|
2013-05-05 19:11:45 +00:00
|
|
|
dotted : Color -> LineStyle
|
2013-03-27 16:52:16 +00:00
|
|
|
dotted clr = { defaultLine | color <- clr, dashing <- [3,3] }
|
2013-03-27 01:50:18 +00:00
|
|
|
|
2013-03-06 17:24:17 +00:00
|
|
|
data BasicForm
|
2013-03-16 08:11:15 +00:00
|
|
|
= FPath LineStyle Path
|
2013-03-06 17:24:17 +00:00
|
|
|
| FShape (Either LineStyle FillStyle) Shape
|
|
|
|
| FImage Int Int (Int,Int) String
|
|
|
|
| FElement Element
|
2013-08-19 18:57:18 +00:00
|
|
|
| FGroup Transform2D [Form]
|
2013-03-06 17:24:17 +00:00
|
|
|
|
2013-06-13 19:00:17 +00:00
|
|
|
form : BasicForm -> Form
|
|
|
|
form f = { theta=0, scale=1, x=0, y=0, alpha=1, form=f }
|
2013-03-16 20:00:59 +00:00
|
|
|
|
2013-07-27 11:06:22 +00:00
|
|
|
fill style shape = form (FShape (Right style) shape)
|
2013-03-06 17:24:17 +00:00
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| Create a filled in shape. -}
|
2013-03-06 17:24:17 +00:00
|
|
|
filled : Color -> Shape -> Form
|
|
|
|
filled color shape = fill (Solid color) shape
|
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| Create a textured shape. The texture is described by some url and is
|
|
|
|
tiled to fill the entire shape.
|
|
|
|
-}
|
2013-03-06 17:24:17 +00:00
|
|
|
textured : String -> Shape -> Form
|
|
|
|
textured src shape = fill (Texture src) shape
|
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| Fill a shape with a [gradient](/docs/Color.elm#linear). -}
|
2013-03-06 17:24:17 +00:00
|
|
|
gradient : Gradient -> Shape -> Form
|
2013-07-29 17:26:53 +00:00
|
|
|
gradient grad shape = fill (Grad grad) shape
|
2013-03-06 17:24:17 +00:00
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| Outline a shape with a given line style. -}
|
2013-03-24 06:54:00 +00:00
|
|
|
outlined : LineStyle -> Shape -> Form
|
2013-07-27 11:06:22 +00:00
|
|
|
outlined style shape = form (FShape (Left style) shape)
|
2013-03-06 17:24:17 +00:00
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| Trace a path with a given line style. -}
|
2013-03-24 06:54:00 +00:00
|
|
|
traced : LineStyle -> Path -> Form
|
|
|
|
traced style path = form (FPath style path)
|
2013-03-16 08:11:15 +00:00
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| Create a sprite from a sprite sheet. It cuts out a rectangle
|
|
|
|
at a given position.
|
|
|
|
-}
|
2013-03-16 08:11:15 +00:00
|
|
|
sprite : Int -> Int -> (Int,Int) -> String -> Form
|
2013-03-21 09:29:23 +00:00
|
|
|
sprite w h pos src = form (FImage w h pos src)
|
2013-03-16 08:11:15 +00:00
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| Turn any `Element` into a `Form`. This lets you use text, gifs, and video
|
|
|
|
in your collage. This means you can move, rotate, and scale
|
|
|
|
an `Element` however you want.
|
|
|
|
-}
|
2013-03-16 08:11:15 +00:00
|
|
|
toForm : Element -> Form
|
2013-03-21 09:29:23 +00:00
|
|
|
toForm e = form (FElement e)
|
2013-03-06 17:24:17 +00:00
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| Flatten many forms into a single `Form`. This lets you move and rotate them
|
|
|
|
as a single unit, making it possible to build small, modular components.
|
|
|
|
-}
|
2013-03-21 09:29:23 +00:00
|
|
|
group : [Form] -> Form
|
2013-07-27 11:06:22 +00:00
|
|
|
group fs = form (FGroup identity fs)
|
2013-03-24 06:54:00 +00:00
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| Flatten many forms into a single `Form` and then apply a matrix
|
|
|
|
transformation.
|
|
|
|
-}
|
2013-08-19 18:57:18 +00:00
|
|
|
groupTransform : Transform2D -> [Form] -> Form
|
2013-03-24 06:54:00 +00:00
|
|
|
groupTransform matrix fs = form (FGroup matrix fs)
|
2013-03-06 17:24:17 +00:00
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| Rotate a form by a given angle. Rotate takes standard Elm angles (radians)
|
|
|
|
and turns things counterclockwise. So to turn `form` 30° to the left
|
|
|
|
you would say, `(rotate (degrees 30) form)`.
|
|
|
|
-}
|
2013-08-11 03:01:48 +00:00
|
|
|
rotate : number -> Form -> Form
|
2013-03-21 09:29:23 +00:00
|
|
|
rotate t f = { f | theta <- f.theta + t }
|
2013-03-06 17:24:17 +00:00
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| Scale a form by a given factor. Scaling by 2 doubles the size.
|
|
|
|
-}
|
2013-08-11 03:01:48 +00:00
|
|
|
scale : number -> Form -> Form
|
2013-05-05 19:11:45 +00:00
|
|
|
scale s f = { f | scale <- f.scale * s }
|
2013-03-21 09:29:23 +00:00
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| Move a form by the given amount. This is a relative translation so
|
|
|
|
`(move (10,10) form)` would move `form` ten pixels up and ten pixels to the
|
|
|
|
right.
|
|
|
|
-}
|
2013-08-11 03:01:48 +00:00
|
|
|
move : (number,number) -> Form -> Form
|
2013-05-16 20:10:50 +00:00
|
|
|
move (x,y) f = { f | x <- f.x + x, y <- f.y + y }
|
2013-05-05 19:11:45 +00:00
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| Move a shape in the x direction. This is relative so `(moveX 10 form)` moves
|
|
|
|
`form` 10 pixels to the right.
|
|
|
|
-}
|
2013-08-11 03:01:48 +00:00
|
|
|
moveX : number -> Form -> Form
|
2013-05-05 19:11:45 +00:00
|
|
|
moveX x f = { f | x <- f.x + x }
|
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| Move a shape in the y direction. This is relative so `(moveY 10 form)` moves
|
|
|
|
`form` upwards by 10 pixels.
|
|
|
|
-}
|
2013-08-11 03:01:48 +00:00
|
|
|
moveY : number -> Form -> Form
|
2013-05-05 19:11:45 +00:00
|
|
|
moveY y f = { f | y <- f.y + y }
|
2013-03-21 09:29:23 +00:00
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| Set the alpha of a `Form`. The default is 1, and 0 is totally transparent. -}
|
2013-08-02 08:53:58 +00:00
|
|
|
alpha : Float -> Form -> Form
|
|
|
|
alpha a f = { f | alpha <- a }
|
2013-06-13 19:00:17 +00:00
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| A collage is a collection of 2D forms. There are no strict positioning
|
|
|
|
relationships between forms, so you are free to do all kinds of 2D graphics.
|
|
|
|
-}
|
2013-03-21 09:29:23 +00:00
|
|
|
collage : Int -> Int -> [Form] -> Element
|
2013-07-29 21:23:04 +00:00
|
|
|
collage = Native.Graphics.Collage.collage
|
2013-04-29 03:57:48 +00:00
|
|
|
|
|
|
|
|
2013-08-11 03:01:48 +00:00
|
|
|
type Path = [(number,number)]
|
2013-04-29 03:57:48 +00:00
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| Create a path that follows a sequence of points. -}
|
2013-08-11 03:01:48 +00:00
|
|
|
path : [(number,number)] -> Path
|
2013-04-29 03:57:48 +00:00
|
|
|
path ps = ps
|
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| Create a path along a given line segment. -}
|
2013-08-11 03:01:48 +00:00
|
|
|
segment : (number,number) -> (number,number) -> Path
|
2013-04-29 03:57:48 +00:00
|
|
|
segment p1 p2 = [p1,p2]
|
|
|
|
|
2013-08-11 03:01:48 +00:00
|
|
|
type Shape = [(number,number)]
|
2013-04-29 03:57:48 +00:00
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| Create an arbitrary polygon by specifying its corners in order.
|
|
|
|
`polygon` will automatically close all shapes, so the given list
|
|
|
|
of points does not need to start and end with the same position.
|
|
|
|
-}
|
2013-08-11 03:01:48 +00:00
|
|
|
polygon : [(number,number)] -> Shape
|
2013-04-29 03:57:48 +00:00
|
|
|
polygon points = points
|
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| A rectangle with a given width and height. -}
|
2013-08-11 03:01:48 +00:00
|
|
|
rect : number -> number -> Shape
|
2013-05-24 07:50:18 +00:00
|
|
|
rect w h = let hw = w/2
|
|
|
|
hh = h/2
|
|
|
|
in [ (0-hw,0-hh), (0-hw,hh), (hw,hh), (hw,0-hh) ]
|
2013-04-29 03:57:48 +00:00
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| A square with a given edge length. -}
|
2013-08-11 03:01:48 +00:00
|
|
|
square : number -> Shape
|
2013-05-24 07:50:18 +00:00
|
|
|
square n = rect n n
|
2013-05-17 20:46:08 +00:00
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| An oval with a given width and height. -}
|
2013-08-11 03:01:48 +00:00
|
|
|
oval : number -> number -> Shape
|
2013-04-29 03:57:48 +00:00
|
|
|
oval w h =
|
|
|
|
let n = 50
|
2013-07-27 11:06:22 +00:00
|
|
|
t = 2 * pi / n
|
2013-04-29 03:57:48 +00:00
|
|
|
hw = w/2
|
|
|
|
hh = h/2
|
2013-07-27 11:06:22 +00:00
|
|
|
f i = (hw * cos (t*i), hh * sin (t*i))
|
2013-05-21 08:03:51 +00:00
|
|
|
in List.map f [0..n-1]
|
2013-04-29 03:57:48 +00:00
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| A circle with a given radius. -}
|
2013-08-11 03:01:48 +00:00
|
|
|
circle : number -> Shape
|
2013-04-29 03:57:48 +00:00
|
|
|
circle r = oval (2*r) (2*r)
|
|
|
|
|
2013-09-12 20:07:38 +00:00
|
|
|
{-| A regular polygon with N sides. The first argument specifies the number
|
|
|
|
of sides and the second is the radius. So to create a pentagon with radius
|
|
|
|
30 you would say:
|
|
|
|
|
|
|
|
ngon 5 30
|
|
|
|
-}
|
2013-08-11 03:01:48 +00:00
|
|
|
ngon : Int -> number -> Shape
|
2013-04-29 03:57:48 +00:00
|
|
|
ngon n r =
|
|
|
|
let m = toFloat n
|
2013-07-27 11:06:22 +00:00
|
|
|
t = 2 * pi / m
|
|
|
|
f i = ( r * cos (t*i), r * sin (t*i) )
|
|
|
|
in List.map f [0..m-1]
|