This repository has been archived by the owner on Feb 2, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathMain.purs
95 lines (85 loc) · 3.24 KB
/
Main.purs
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
module Test.Main where
import Prelude
import Color (lighten)
import Color.Scheme.MaterialDesign (blueGrey)
import Data.Array (sortBy, (..))
import Data.Foldable (foldMap)
import Data.Int (toNumber)
import Data.Maybe (fromJust, maybe)
import Data.Newtype (unwrap)
import Data.Set (isEmpty)
import Effect (Effect)
import FRP.Behavior (Behavior, animate, fixB, integral', switcher)
import FRP.Behavior.Mouse (buttons)
import FRP.Behavior.Mouse as Mouse
import FRP.Behavior.Time as Time
import FRP.Event.Mouse (Mouse, getMouse, down)
import Global (infinity)
import Graphics.Canvas (getCanvasElementById, getCanvasHeight, getCanvasWidth, getContext2D, setCanvasHeight, setCanvasWidth)
import Graphics.Drawing (Drawing, circle, fillColor, filled, lineWidth, outlineColor, outlined, rectangle, render, scale, translate)
import Partial.Unsafe (unsafePartial)
type Circle = { x :: Number, y :: Number, size :: Number }
scene :: Mouse -> { w :: Number, h :: Number } -> Behavior Drawing
scene mouse { w, h } = pure background <> map renderCircles circles where
background :: Drawing
background = filled (fillColor blueGrey) (rectangle 0.0 0.0 w h)
scaleFactor :: Number
scaleFactor = max w h / 16.0
renderCircle :: Circle -> Drawing
renderCircle { x, y, size } =
scale scaleFactor scaleFactor <<< translate x y <<< scale size size $
outlined
(outlineColor (lighten (0.2 + size * 0.2) blueGrey) <> lineWidth ((1.0 + size * 2.0) / scaleFactor))
(circle 0.0 0.0 0.5)
renderCircles :: Array Circle -> Drawing
renderCircles = foldMap renderCircle
-- `swell` is an interactive function of time defined by a differential equation:
--
-- d^2s/dt^2
-- | mouse down = ⍺ - βs
-- | mouse up = ɣ - δs - ε ds/dt
--
-- So the function exhibits either decay or growth depending on if
-- the mouse is pressed or not.
--
-- We can solve the differential equation by integration using `solve2'`.
swell :: Behavior Number
swell =
fixB 2.0 \b ->
integral' 2.0 (unwrap <$> Time.seconds)
let db = fixB 10.0 \db_ ->
integral' 10.0 (unwrap <$> Time.seconds) (f <$> buttons mouse <*> b <*> db_)
in switcher db (down $> db)
where
f bs s ds | isEmpty bs = -8.0 * (s - 1.0) - ds * 2.0
| otherwise = 2.0 * (4.0 - s)
circles :: Behavior (Array Circle)
circles = toCircles <$> Mouse.position mouse <*> swell where
toCircles m sw =
sortBy (comparing (\{ x, y } -> -(dist x y m))) do
i <- 0 .. 16
j <- 0 .. 16
let x = toNumber i
y = toNumber j
d = dist x y m
pure { x
, y
, size: 0.1 + (1.0 + sw) / (d + 1.5)
}
where
dist x y = maybe infinity \{ x: mx, y: my } ->
let dx = x - toNumber mx / scaleFactor
dy = y - toNumber my / scaleFactor
in dx * dx + dy * dy
main :: Effect Unit
main = do
mcanvas <- getCanvasElementById "canvas"
let canvas = unsafePartial (fromJust mcanvas)
ctx <- getContext2D canvas
w <- getCanvasWidth canvas
h <- getCanvasHeight canvas
_ <- setCanvasWidth canvas w
_ <- setCanvasHeight canvas h
mouse <- getMouse
_ <- animate (scene mouse { w, h }) (render ctx)
pure unit