-
Notifications
You must be signed in to change notification settings - Fork 0
/
Teapot.elm
191 lines (132 loc) · 4.53 KB
/
Teapot.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
module Teapot where
import Geometry
import Svg exposing (..)
import Svg.Attributes exposing (..)
import Html exposing (Html)
import Time
import TeapotModel as Model
-- SIGNALS
type Action = NoOp | Tick Float | Boom Bool | DoBFC Bool | DoIllumination Bool | DoRotation Bool
clock : Signal Action
clock =
Signal.map Tick (Time.fps 24)
-- PORTS
port dnp : Signal Bool
port bfc : Signal Bool
port ill : Signal Bool
port rot : Signal Bool
port fps : Signal Int
port fps =
(Signal.map (\m -> truncate (1000.0 / m.delta)) model)
-- VIEW
translate : Geometry.Point2D -> Geometry.Point2D
translate point2D =
Geometry.translate2D 20.0 16.0 point2D
zoom : Geometry.Point2D -> Geometry.Point2D
zoom point2D =
Geometry.zoom 10.0 point2D
project : Geometry.Point3D -> Geometry.Point2D
project point3D =
Geometry.project 0.2 point3D
transform : Geometry.Point3D -> Geometry.Point3D -> Geometry.Point3D
transform rotation point3D =
point3D
|> Geometry.rotateX (Geometry.degToRad rotation.x)
|> Geometry.rotateY (Geometry.degToRad rotation.y)
|> Geometry.rotateZ (Geometry.degToRad rotation.z)
transformFace : Geometry.Point3D -> Geometry.Poly -> Geometry.Poly
transformFace rotation face =
{ face | vertices <- (List.map (transform rotation) face.vertices) }
toScreen : Geometry.Point3D -> Geometry.Point2D
toScreen point3D =
point3D
|> project
|> zoom
|> translate
faceToScreen : Geometry.Poly -> (Float, List Geometry.Point2D)
faceToScreen face =
(face.illumination, List.map toScreen face.vertices)
pToStr : Geometry.Point2D -> String
pToStr p = (toString p.x) ++ "," ++ (toString p.y)
pvecToStr : List Geometry.Point2D -> String
pvecToStr pvec = List.map pToStr pvec
|> List.intersperse " "
|> List.foldl (++) ""
polyToSvg : (Float, List Geometry.Point2D) -> Svg
polyToSvg (light, poly) =
polygon [ fill (illuminationToColour light), points (pvecToStr poly) ] []
facesCamera : Geometry.Poly -> Bool
facesCamera face =
let n = Geometry.normal face
in
n.z > 0
light : Geometry.Poly -> Float
light face =
Geometry.cosAngle (Geometry.normal face) Model.lightVector
illuminationToColour : Float -> String
illuminationToColour l =
let r = toString( round (64 + 128 * l) )
g = toString( round (32 + 64 * l) )
b = toString( round (64 + 80 * l) )
in
"rgb(" ++ r ++ "," ++ g ++ "," ++ b ++ ")"
illuminate : Geometry.Poly -> Geometry.Poly
illuminate face =
{ face | illumination <- (light face) }
drawTeapot : Model.Model -> Html
drawTeapot model =
svg [ version "1.1", x "0", y "0", viewBox "0 0 100 100" ]
(model.faces
|> List.map (transformFace model.rotation)
|> (if model.bfcOn then (List.filter (\face -> facesCamera face)) else (identity))
|> List.sortBy Geometry.meanZ
|> (if model.illuminationOn then (List.map illuminate) else (identity))
|> List.reverse
|> List.map faceToScreen
|> List.map polyToSvg)
-- UPDATE
updateVertex : Geometry.Point3D -> Geometry.Point3D -> Geometry.Point3D
updateVertex v velocity =
Geometry.Point3D (v.x + velocity.x) (v.y + velocity.y) (v.z + velocity.z)
updateFace : Geometry.Poly -> Geometry.Poly
updateFace poly =
{ poly | vertices <- (List.map (updateVertex poly.velocity) poly.vertices) }
update : Action -> Model.Model -> Model.Model
update action model =
case action of
NoOp ->
model
Boom _ ->
explode model
DoBFC a ->
{ model | bfcOn <- a }
DoIllumination a ->
{ model | illuminationOn <- a }
DoRotation a ->
{ model | rotationOn <- a }
Tick delta ->
if model.rotationOn then
{ model | rotation <- Geometry.Point3D (model.rotation.x + 0) (model.rotation.y + 10) (model.rotation.z + 0)
, faces <- (List.map updateFace model.faces)
, delta <- delta}
else
model
model : Signal Model.Model
model =
Signal.foldp update Model.initialModel actions
actions : Signal Action
actions =
Signal.mergeMany [clock, (Signal.map Boom dnp), (Signal.map DoBFC bfc), (Signal.map DoIllumination ill), (Signal.map DoRotation rot)]
-- MAIN
explodePoly : Geometry.Poly -> Geometry.Poly
explodePoly poly =
let
newVelocity = (Maybe.withDefault (Geometry.Point3D 0 0 0) (List.head poly.vertices))
in
{ poly | velocity <- (Geometry.scale 0.5 (Geometry.Point3D (newVelocity.x) (newVelocity.y + 0.5) (newVelocity.z))) }
explode : Model.Model -> Model.Model
explode model =
{ model | faces <- List.map explodePoly model.faces }
main : Signal Html
main =
Signal.map drawTeapot model