-
Notifications
You must be signed in to change notification settings - Fork 2
/
random-points-on-sphere.hs
73 lines (61 loc) · 2.8 KB
/
random-points-on-sphere.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
import System.IO ()
import System.Random
import Graphics.Gloss
import Graphics.Gloss.Geometry.Angle
window :: Display
window = InWindow "random points on sphere with Haskell" (600, 450) (20, 20)
dot :: Color -> Point -> Picture
dot col (x, y) = Color col $ translate x y $ thickCircle 1.0 2.0
redDot :: Point -> Picture
redDot (x, y) = dot red (x, y)
type Point3D = (Float, Float, Float)
polarToCartesian :: Float -> Float -> Float -> Point3D
polarToCartesian theta gamma radius = (x, y, z)
where thetaRad = degToRad theta
gammaRad = degToRad gamma
sinTheta = sin thetaRad
x = radius * sinTheta * cos gammaRad
y = radius * sinTheta * sin gammaRad
z = radius * cos thetaRad
sphere :: [Point3D]
sphere = map (\(t, g) -> polarToCartesian t g 250.0) randomAnglePairs
where randomAnglePairs = zip thetas gammas
thetas = take 750 $ randomRs (0.0::Float, 360.0) $ mkStdGen 42
gammas = take 750 $ randomRs (0.0::Float, 360.0) $ mkStdGen 1337
rotateX :: Float -> Point3D -> Point3D
rotateX degree (x, y, z) = (x, c*y + s*z, -s*y + c*z)
where radiant = normalizeAngle $ degToRad degree
c = cos radiant
s = sin radiant
rotateZ :: Float -> Point3D -> Point3D
rotateZ degree (x, y, z) = (c*x + s*y, -s*x + c*y, z)
where radiant = normalizeAngle $ degToRad degree
c = cos radiant
s = sin radiant
project :: Point3D -> Point
project (x0, y0, z0) = (projectedX, projectedY)
where (lookAtX, lookAtY, lookAtZ) = (0.0, 0.0, 0.0)
(x, y, z) = (x0 - lookAtX, y0 - lookAtY, z0 - lookAtZ)
(alpha, beta, gamma) = (degToRad 0.0, degToRad 0.0, degToRad 0.0)
(eyeX, eyeY, eyeZ) = (0.0, 0.0, 300.0)
(cosAlpha, sinAlpha) = (cos alpha, sin alpha)
(cosBeta, sinBeta) = (cos beta, sin beta)
(cosGamma, sinGamma) = (cos gamma, sin gamma)
(dx, dy, dz) = (cosBeta*(sinGamma*y + cosGamma*x) - sinBeta*z,
sinAlpha*(cosBeta*z + sinBeta*(sinGamma*y + cosGamma*x)) +
cosAlpha*(cosGamma*y - sinGamma*x),
cosAlpha*(cosBeta*z + sinBeta*(sinGamma*y + cosGamma*x)) -
sinAlpha*(cosGamma*y - sinGamma*x))
projectedX = eyeZ/dz*dx - eyeX
projectedY = eyeZ/dz*dy - eyeY
move :: Point3D -> Point3D -> Point3D
move (dx, dy, dz) (x, y, z) = (x + dx, y + dy, z + dz)
frame :: Float -> Picture
frame seconds = pictures (map redDot imageSpaceCoords)
where offset = move (0.0, 0.0, 400.0)
rotate' = rotateX (seconds * 35.0) .
rotateZ (seconds * 45.0)
moved = map (offset . rotate') sphere
imageSpaceCoords = map project moved
main :: IO ()
main = animate window white frame