-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCircleAvoid.elm
122 lines (98 loc) · 3.07 KB
/
CircleAvoid.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
module CircleAvoid exposing (..)
import List exposing (map, foldl, filterMap, minimum)
import Color exposing (..)
import Collage exposing (..)
import Element exposing (Element)
import Time exposing (Time, inSeconds)
import Vec2 exposing (..)
import ClassicalEngine exposing (..)
--- Structures ---
type alias Driver = {
vehicle : Actor {},
vision : OBR {} -- a box represents their line of sight
}
type alias Simulation = {
driver : Driver,
terrain : List Circle
}
--- Constants ---
terrain : List Circle
terrain = [
{o = (-200, -200), r = 50},
{o = (200, -200), r = 50},
{o = (-200, 200), r = 50},
{o = (200, 200), r = 50},
{o = (50, -70), r = 80},
{o = (-120, 120), r = 25},
{o = (-85, 25), r = 40},
{o = (12, 107), r = 50},
{o = (123, 55), r = 30},
{o = (-125, -75), r = 30},
{o = (-75, -145), r = 13},
{o = (123, 130), r = 13}
]
maxA : Float
maxA = 80
maxV : Float
maxV = 120
maxBrakeTime : Float
maxBrakeTime = maxV / (2 * maxA)
-- Behavior --
-- generates line of sight based on current speed
futureProj : Actor etc -> OBR {}
futureProj actor = let
r = maxBrakeTime *. actor.v
d = norm r
n = r ./ d
in
{ o = actor.pos .+. r ./ 2, dir = n, size = (d, 16) }
-- distance from actor and projected point of collision, or nothing
nearestFutureCollision : Simulation -> Maybe Vec2
nearestFutureCollision sim =
filterMap (collideOBRxCircle sim.driver.vision) sim.terrain |>
map (\p -> (norm (sim.driver.vehicle.pos .-. p), p)) |>
minimum |> Maybe.map snd
avoid : Vec2 -> Driver -> Driver
avoid p driver = let
r = p .-. driver.vehicle.pos
dir = driver.vision.dir
urgency = fst driver.vision.size / norm r / 2
avoidV = perp dir .* if dir `cross` r > 0 then -urgency else urgency
steering = avoidV .* maxV .-. driver.vehicle.v
braking = urgency * -urgency *. dir
newA = 2 * maxA *. (steering .+. braking) |> clamp2 0 maxA
oldActor = driver.vehicle -- needed to get around parser limitation
newActor = { oldActor | a = newA } -- can't just put driver.actor here :\
in
{ driver | vehicle = newActor }
accel : Driver -> Driver
accel driver = let oldActor = driver.vehicle in
{ driver | vehicle = { oldActor | a = normalize oldActor.v .* maxA } }
-- Simulation --
simulate : Time -> Simulation -> Simulation
simulate t sim = let
dt = (inSeconds t)
evasiveDriver = case (nearestFutureCollision sim) of
Just p -> sim.driver |> avoid p
Nothing -> sim.driver |> accel
movedActor = stepActor maxV dt evasiveDriver.vehicle
newActor = { movedActor |
pos = wrap2 (-200, -200) (200, 200) movedActor.pos }
newOBR = futureProj newActor
in { sim | driver =
{ evasiveDriver | vehicle = newActor, vision = newOBR } }
initSim : Simulation
initSim = let initActor = { pos = (0, 10), v = (maxV, 0), a = (0, 0) } in
{
driver = {
vehicle = initActor,
vision = futureProj initActor
},
terrain = terrain
}
-- Drawing --
drawSim : Simulation -> Element
drawSim sim = collage 400 400 <|
(foldl (++) (drawVehicle green sim.driver.vehicle) <|
map (drawObstacle grey) sim.terrain) ++
drawOBR (solid yellow) sim.driver.vision