-
Notifications
You must be signed in to change notification settings - Fork 0
/
ClassicalEngine.elm
119 lines (96 loc) · 3.19 KB
/
ClassicalEngine.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
module ClassicalEngine exposing (..)
import Vec2 exposing (..)
import Collage exposing (..)
import Color exposing (Color, charcoal, red, blue)
--- Structures ---
-- a basic kinematic entity
type alias Actor etc = { etc |
pos : Vec2,
v : Vec2,
a : Vec2
}
actor0 : Actor {}
actor0 = { pos = (0, 0), v = (0, 0), a = (0, 0) }
-- just a circle. origin and radius.
type alias Circle = {
o : Vec2,
r : Float
}
circle0 : Circle
circle0 = { o = (0, 0), r = 0 }
-- Oriented Bounding Rectangle, a rectangle in 2D
type alias OBR etc = { etc |
o : Vec2, -- center point of the rectangle
dir : Vec2, -- local x axis; local y is perp to this
size : Vec2 -- width and height
}
obr0 : OBR {}
obr0 = { o = (0, 0), dir = (0, 0), size = (0, 0) }
--- Kinematics ---
-- update movement of actor based on velocity and acceleration
-- really basic classical motion, no Verlet or anything here, sorry
stepActor : Float -> Float -> Actor etc -> Actor etc
stepActor maxV dt actor = { actor |
pos = actor.pos .+. actor.v .* dt,
v = actor.v .+. actor.a .* dt |> clamp2 0 maxV
}
-- circular wrapping (one dimension)
wrap : Float -> Float -> Float -> Float
wrap min max x = let len = max - min in
if x < min then x + len else if x > max then x - len else x
-- toroidal wrapping (two dimensions)
wrap2 : Vec2 -> Vec2 -> Vec2 -> Vec2
wrap2 (xmin, ymin) (xmax, ymax) (x, y) =
(wrap xmin xmax x, wrap ymin ymax y)
--- Collision ---
-- closest point in rectangle from center to given point
nearestPointOBR : OBR etc -> Vec2 -> Vec2
nearestPointOBR obr p = let
d = p .-. obr.o
-- normals/axes of rectangle
nx = obr.dir
ny = perp obr.dir
-- apothems/extents of rectangle
rx = fst obr.size / 2
ry = snd obr.size / 2
in
(d `dot` nx |> clamp -rx rx) *. nx .+.
(d `dot` ny |> clamp -ry ry) *. ny .+. obr.o
-- if they intersect, gives closest point to circle within rect
collideOBRxCircle : OBR etc -> Circle -> Maybe Vec2
collideOBRxCircle obr circ = let p = nearestPointOBR obr circ.o in
if sqnorm (p .-. circ.o) > circ.r * circ.r then Nothing else Just p
--- Drawing ---
drawVehicle : Color -> Actor etc -> List Form
drawVehicle color actor = [
circle 8 |> filled color |> move actor.pos,
circle 8 |> outlined (solid charcoal) |> move actor.pos,
traced (solid red) <| segment actor.pos (actor.pos .+. actor.v),
traced (solid blue) <| segment actor.pos (actor.pos .+. actor.a)
]
drawBoid : Color -> Bool -> Actor etc -> List Form
drawBoid color showV actor = let
vdir = normalize actor.v
vperp = perp vdir
boidtriangle = polygon [
actor.pos .+. 10 *. vdir,
actor.pos .-. 3 *. vdir .+. 4 *. vperp,
actor.pos .-. 3 *. vdir .-. 4 *. vperp
]
in [
boidtriangle |> filled color,
boidtriangle |> outlined (solid charcoal)
] ++ if showV then [
segment actor.pos (actor.pos .+. actor.a) |> traced (solid blue)
] else []
drawObstacle : Color -> Circle -> List Form
drawObstacle color circ = [
circle circ.r |> filled color |> Collage.move circ.o,
circle circ.r |> outlined (solid charcoal) |> Collage.move circ.o
]
drawOBR : LineStyle -> OBR etc -> List Form
drawOBR style obr = [
outlined style (uncurry rect obr.size) |>
Collage.rotate (uncurry (flip atan2) obr.dir) |>
Collage.move obr.o
]