-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.hs
105 lines (98 loc) · 3.17 KB
/
main.hs
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
{-# Language TemplateHaskell #-}
module Main where
-- base
import Data.Monoid
import Control.Applicative
-- linear
import Linear
-- lens
import Control.Lens
-- gloss
import Graphics.Gloss
import Graphics.Gloss.Interface.Pure.Game
-- colour
import Data.Colour (Colour)
import Data.Colour.RGBSpace
import Data.Colour.RGBSpace.HSV
-- falling
import Falling
-- | The interface can be in two states: one, where everything
-- is just running by itself, and another, where we're adding
-- and changing things about one particular particle.
data Interface n
= Running
{ _particles :: [Particle n]
}
| Adding
{ _adding :: Particle n
, _particles :: [Particle n]
}
deriving
( Eq
, Ord
, Show
)
makeLenses ''Interface
makePrisms ''Interface
-- | Do something with input.
withInput :: Event -> Interface Float -> Interface Float
-- If we get a mouseclick down in 'Running', add a particle at the
-- place of the click and switch to 'Adding'; we'll be editing
-- it until the corresponding mouseclick up.
withInput (EventKey (MouseButton LeftButton) Down _ (x', y'))
(Running ps) = Adding (particle $ V3 x' y' 0) ps
-- If we get a mouse movement while in 'Adding', change the
-- velocity of the new particle to the new place of the mouse;
-- keep in mind the offset of particle.
withInput (EventKey (MouseButton LeftButton) _ _ _)
(Adding p ps) = Running (p : ps)
-- If we get a mouseclick up while in 'Adding', push the particle
-- into the list of particles and switch to 'Running'.
withInput (EventMotion (x', y')) (Adding p ps) = Adding
(velocity .~ view place p - V3 x' y' 0 $ p) ps
-- Otherwise just ignore the input.
withInput _ i = i
-- Draw an interface.
redraw :: Interface Float -> Picture
redraw i = foldMapOf (particles . traverse) single i
<> case i of
Running _ -> mempty
-- If we're in an 'Adding', draw the new particle
-- and a line indicating its velocity.
Adding p _ -> let
-- The vector being drawn from the particle.
d :: V3 Float
d = p ^. place - p ^. velocity
-- The color to draw the vector in.
c :: Color
c = uncurryRGB makeColor
(hsv (angle (d ^. _x) (d ^. _y)) 0.5 0.5) 1 in
-- Draw the particle we're adding and a line from it
-- to the mouse, representing the inverse of its
-- velocity.
single p <> Color c (Line
[ (p ^. place . _x, p ^. place . _y)
, (d ^. _x, d ^. _y)
])
where
-- Radians to degrees.
degrees :: Floating t => t -> t
degrees = (*) (180 / pi)
-- Angle of a vector, in degrees.
angle :: Floating t => t -> t -> t
angle x y = degrees . atan $ y / x
-- Draw a single particle as a white circle.
single :: Particle Float -> Picture
single = Translate <$> (^. place . _x)
<*> (^. place . _y)
?? Color white (circleSolid 2.0)
-- A massive particle at the origin.
sun :: Particle Float
sun = Particle 0 0 200
-- Iterate the world.
iteration :: Float -> Interface Float -> Interface Float
iteration t i = if has _Adding i then i
else particles %~ update . map (move t) $ i
main :: IO ()
main = play (InWindow "falling!" (300, 300) (100, 100))
black 40 (Running [sun]) redraw withInput iteration