Instantly share code, notes, and snippets.

@ggb ggb/Particle.elm
Last active Sep 5, 2016

Embed
What would you like to do?
module Particle exposing (..)
import Array
import Task
import Html.App as App
import Window exposing (Size, resizes)
import Color exposing (Color, black, white, rgba)
import Element exposing (Element, toHtml)
import Collage exposing (Form, collage, move, rect, filled, circle)
import AnimationFrame exposing (times)
import Mouse exposing (Position, moves)
import Time exposing (Time)
import Random exposing (Generator, generate, list, float)
import Basics.Extra exposing (never)
type Msg
= Move Position
| Frame Time
| Resize Size
| Add (List Float)
type alias Particle =
{ x: Float
, y: Float
, dx: Float
, dy: Float
, color: Color
, radius: Float
}
type alias ParticleField =
{ particles: List Particle
, size: Size
, lastPosition: Position
}
initField : ParticleField
initField =
{ particles = []
, size = {width=600, height=600}
, lastPosition = {x=0, y=0}
}
floatList : Generator (List Float)
floatList =
list 5 (float 0 1)
subscriptions : ParticleField -> Sub Msg
subscriptions model =
Sub.batch
[ moves Move
, times Frame
, resizes Resize
]
drawParticle : Particle -> Form
drawParticle particle =
move
(particle.x, particle.y)
(filled particle.color (circle particle.radius))
draw : ParticleField -> Element
draw {particles, size} =
particles
|> List.map drawParticle
-- |> (::) (filled white (rect (toFloat size.width) (toFloat size.height)))
|> collage size.width size.height
split : Int -> List a -> List (List a)
split =
let
split' acc num origin =
if List.length origin == 0 then
acc
else
split' (List.take num origin :: acc) num (List.drop num origin)
in
split' []
randomColor : Float -> Float -> Color
randomColor m n =
let
m' = m * 205 |> round
n' = n * 205 |> round
in
if (m' + n') % 2 == 0 then
rgba (50 + m') (50 + n') 0 (0.25 + ((m + n) / 8))
else
rgba 0 (50 + m') (50 + n') (0.25 + ((m + n) / 8))
createParticle : Position -> Size -> List Float -> Particle
createParticle {x,y} {width, height} l =
let
arr = Array.fromList l
sget i = Array.get i arr |> Maybe.withDefault 0
in
{ x = (toFloat x) - (toFloat width / 2)
, y = (toFloat y) * (-1) + (toFloat height / 2)
, dx = -1.5 + (sget 0) * 3
, dy = 1 + (sget 1 + (1 - sget 4)) * 3
, color = randomColor (sget 2) (sget 3)
, radius = 1 + (sget 4) * 21
}
addParticles : List Float -> ParticleField -> ParticleField
addParticles numbers field =
let
newParticles =
numbers
|> split 5
|> List.map (createParticle field.lastPosition field.size)
in
{field | particles = newParticles ++ field.particles |> List.take 200 }
updatePositions : ParticleField -> ParticleField
updatePositions field =
{ field | particles
= field.particles
|> List.map (\p -> {p | x = p.x - p.dx, y = p.y - p.dy})
}
update : Msg -> ParticleField -> (ParticleField, Cmd Msg)
update msg field =
case msg of
Move newPosition ->
({field | lastPosition = newPosition}, generate Add floatList)
Frame t ->
updatePositions field ! []
Resize newSize ->
{field | size = newSize} ! []
Add numbers ->
addParticles numbers field ! []
main : Program Never
main =
App.program
{ init = (initField, Task.perform never Resize Window.size)
, view = draw >> toHtml
, update = update
, subscriptions = subscriptions
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment