-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathFlocking.elm
132 lines (105 loc) · 3.34 KB
/
Flocking.elm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
module Flocking exposing (..)
import Vec2 exposing (..)
import ClassicalEngine exposing (..)
import Random exposing (Generator, generate, initialSeed, Seed)
import Collage exposing (collage)
import Element exposing (Element)
import Time exposing (Time, inSeconds)
import Color exposing (grey)
--- Structures ---
type alias Parameters = {
maxA : Float,
maxV : Float,
separation : Float,
alignment : Float,
coherence : Float,
neighborhoodRadius : Float,
drawVectors : Bool
}
type alias Simulation = {
params : Parameters,
boids : List (Actor {})
}
sim0 : Simulation
sim0 = { params = defaults, boids = [] }
type alias FlockBehavior etc = Float -> Actor etc -> List (Actor etc) -> Vec2
--- Constants ---
defaults : Parameters
defaults = {
maxA = 70,
maxV = 120,
separation = 20,
alignment = 15,
coherence = 8,
neighborhoodRadius = 90,
drawVectors = True
}
canvasW : Int
canvasW = 600
canvasH : Int
canvasH = 400
maxX : Float
maxX = toFloat canvasW / 2
minX : Float
minX = negate maxX
maxY : Float
maxY = toFloat canvasH / 2
minY : Float
minY = negate maxY
--- Behavior ---
-- gets all boids in flock within given radius
neighborhood : Float -> Actor etc -> List (Actor etc) -> List (Actor etc)
neighborhood r boid = List.filter
(\a -> a.pos /= boid.pos && dist a.pos boid.pos <= r)
-- avoid a neighbor more the closer it is
avoision : Float -> Actor etc -> Actor etc -> Vec2
avoision r boid neighbor = let
s = boid.pos .-. neighbor.pos
in
r *. s ./ sqnorm s
-- steering away from neighbors, inversely related to distance
separation : FlockBehavior etc
separation r boid = neighborhood r boid
>> List.map (avoision r boid)
>> List.foldl (.-.) (0, 0)
-- steering toward the average of neighbors' directions
alignment : FlockBehavior etc
alignment r boid = neighborhood r boid
>> List.foldl (\a dirs -> dirs .+. a.v) (0, 0)
>> clamp2 0 1
-- steering toward the average of neighbors' positions
cohesion : FlockBehavior etc
cohesion r boid = neighborhood r boid
>> List.foldl (\a poss -> poss .+. (a.pos .-. boid.pos)) (0, 0)
>> clamp2 0 1
-- combine the above, weighted
flocking : Float -> Float -> Float -> FlockBehavior etc
flocking a b c r boid flock =
a *. separation (r / 3) boid flock .+.
b *. alignment r boid flock .+.
c *. cohesion (r * 2 / 3) boid flock
--- Simulation ---
initSim : Parameters -> Generator Simulation
initSim params = let
maxRandV = params.maxV / 4
genPos = Vec2.random (minX, minY) (maxX, maxY)
genV = Vec2.random (-maxRandV, -maxRandV) (maxRandV, maxRandV)
in Random.map2 (\poss vs -> {
params = params,
boids = List.map2 (\pos v -> { pos = pos, v = v, a = (0, 0) }) poss vs
}) (Random.list 60 genPos) (Random.list 60 genV)
simulate : Time -> Simulation -> Simulation
simulate t sim = let
dt = (inSeconds t)
{separation, alignment, coherence, neighborhoodRadius} = sim.params
neighbors = sim.boids
in { sim | boids = List.map (\boid -> { boid | a =
flocking separation alignment coherence neighborhoodRadius
boid sim.boids |> clamp2 0 80
} |> stepActor 120 dt |> worldWrap ) sim.boids }
worldWrap : Actor etc -> Actor etc
worldWrap boid = { boid | pos = wrap2 (minX, minY) (maxX, maxY) boid.pos }
--- Drawing ---
drawSim : Simulation -> Element
drawSim sim = collage canvasW canvasH <| List.foldl (++) []
<| List.map (drawBoid grey sim.params.drawVectors) sim.boids