Haskell Gloss Particle Effects - haskell

How to create particle effects in Haskell using the Gloss library? (e.g. to show an explosion)
If anyone could help me out a bit on how this is done it'd be much appreciated.
Best Regards,
Skyfe.

The comment on the question does a good job of providing a high-level solution, but I'm writing this answer to add detail.
Let's start by modeling the real-world object we want to represent. In our case, it's a particle. A particle ought to have a position, a velocity and an acceleration, all of which we can represent using 2D vectors. A reasonable way to store 2D vectors in Haskell is to use the Linear.V2 module. Next, let's think about nice additional properties we'd like a particle should have, specifically one involved in a firework or explosion. Notice how the particles in a firework burn bright for a time and then just 'fizzle out'? Let's call said time the particle's lifespan, and represent it using a Float. We can now create an appropriate representation for a Particle and a Cluster of Particles
data Particle = Particle
{ _age :: Float
, _lifespan :: Float
, _position :: V2 Float
, _velocity :: V2 Float
, _acceleration :: V2 Float }
deriving ( Show )
type Cluster = [Particle]
makeLenses ''Particle
There's an extra field called age in our datatype above. The lifespan of the particle represents the time for which the particle exists from creation to death, while its age represents the time that has passed since the Particle's creation. In other words, a Particle should disappear when its age exceeds its lifespan. Keep that in mind for later.
Next, let's write a function that helps us create a Particle. All it does is set the initial age to 0 and leave the rest up to additional arguments
makeParticle :: Float -> V2 Float -> V2 Float -> V2 Float -> Particle
makeParticle = Particle 0
Once this is done, we can write a function that helps us create a Cluster of n particles
makeCluster :: Int -> (Int -> Particle) -> Cluster
makeCluster n particleGen = map particleGen [0..(n - 1)]
After that, we create a function that will allow us to advance a Particle by dt seconds. The function advances the Particle's
age, changes its position based on its velocity and finally changes its velocity based on its acceleration. In the end, if the age of the Particle is more than its lifespan, we symbolize the deletion of the Particle by evaluating to Nothing instead of Just the changed particle.
advanceParticle :: Float -> Particle -> Maybe Particle
advanceParticle dt = hasDecayed . updateVel . updatePos . updateAge
where
r2f = realToFrac
hasDecayed p = if p^.age < p^.lifespan then Just p else Nothing
updateAge p = (age %~ (dt +)) p
updatePos p = (position %~ (r2f dt * p^.velocity +)) p
updateVel p = (velocity %~ (r2f dt * p^.acceleration +)) p
The following function advances a Cluster, and gets rid of 'dead' Particles
advanceCluster :: Float -> Cluster -> Cluster
advanceCluster dt = catMaybes . map (advanceParticle dt)
Now we can move on to the part of the code that has to do with actually drawing particles using Graphics.Gloss. We're going to use a Cluster to represent the state of the simulation, and so we start with a function that returns a Cluster representing the initial state of the program. For a simple animation we're going to simulate a firework, where all the particles start in the same position, have the same lifespan, radiate out from their central position at regular angles, and are subject to the same acceleration
initState :: Cluster
initState = makeCluster numParticles particleGen
where
numParticles = 10
particleGen :: Int -> Particle
particleGen i =
makeParticle initLifespan
initPosition
(initVelMagnitude * V2 (cos angle) (sin angle))
initAcceleration
where
fI = fromIntegral
angle = (fI i) * 2 * pi / (fI numParticles)
initLifespan = 10
initPosition = V2 0 0
initVelMagnitude = 5
initAcceleration = V2 0 (-3)
Then we write a function to draw a Cluster on to the screen
drawState :: Cluster -> Picture
drawState = pictures . map drawParticle
where
drawParticle :: Particle -> Picture
drawParticle p =
translate (p^.position._x) (p^.position._y) .
color (colorAdjust (p^.age / p^.lifespan)) .
circleSolid $ circleRadius
where
circleRadius = 3
colorAdjust a = makeColor 1 0 0 (1 - a)
Probably the only non-standard part about this is the colorAdjust function. What I was going for here was to color a Particle red and when it's created have it not be transparent at all (i.e. alpha value of 1) and keep fading out as its age approaches its lifespan (i.e. alpha value that keeps approaching 0)
We're almost done! Add a function that updates the Cluster to reflect the passage of time
stepState :: ViewPort -> Float -> Cluster -> Cluster
stepState _ = advanceCluster
Finish up the program by writing a main function that ties everything together
main :: IO ()
main =
simulate (InWindow name (windowWidth, windowHeight)
(windowLocX, windowLocY))
bgColor
stepsPerSec
initState
drawState
stepState
where
name = "Fireworks!"
windowWidth = 300
windowHeight = 300
windowLocX = 30
windowLocY = 30
stepsPerSec = 30
bgColor = white
I hope this helps!

Related

Haskell Translation Task

I'm new to programming, and I'm having trouble solving a task.
I have to use the function. In that case I have to implement it on a triangle.
I've tried different things but I'm just getting errors and that's why I'd like to ask for help.
data Triangle = Triangle {
tP1 :: Point,
tP2 :: Point,
tP3 :: Point}
deriving (Show)
First, points and vectors are two separate concepts, and should probably be distinct types, not just two different aliases for a 2-tuple.
data Point = Pt Float Float
data Vector = V Float Float
Second, your type class seems to capture the idea of translating collections of points using the same vector. The return type should then be the same as the first argument type, not hard-coded to Point.
class Polygon p where
translatePol :: p -> VectorD -> p
Now you can start simple, and define a Polygon instance for Point. (Think of a point as a degenerate polygon.)
instance Polygon Point where
translatePol (Pt x y) (Mvector v1 v2) = Pt (x + v1) (y + v2)
This can be used to define the instance for Triangle more simply.
instance Polygon Triangle where
translatePol (MTriangle p1 p2 p3) v = MTriangle (t p1) (t p2) (t p3)
where t p = translatePol p v

Idiomatic way to express general computations in Haskell

There must exist a good idiomatic way to express general computations in Haskell on type level. All I can come up with is this (illegal) OO imitation.
class Computation where
compute :: Computation -> Double -> Double
data Id = Id
instance Computation Id where
compute _ = id
data Square a = Computation a => Square a
instance Computation (Square a) where
compute (Square underlying) x = sqr $ compute underlying x where square x = x*x
data Scale a = Computation a => Scale a Double
compute (Scale underlying c) x = c * compute underlying x
Ideally, I would like to retain openness, so this approach doesn't appeal to me. Am I asking for too much?
You can certainly do it with the approach you have, you just need to get the syntax and some of the details right, but this certainly works:
class Computation a where
compute :: a -> Double
instance Computation Double where
compute = id
data Square a = Square a
instance Computation a => Computation (Square a) where
compute (Square underlying) = square $ compute underlying where square i = i * i
data Scale a = Scale a Double
instance Computation a => Computation (Scale a) where
compute (Scale underlying c) = c * compute underlying
data Add a = Add a Double
instance Computation a => Computation (Add a) where
compute (Add underlying c) = c + compute underlying
test :: Add (Scale (Scale (Square Double)))
test = Add (Scale (Scale (Square 2) 5) 0.5) 100
main :: IO ()
main = print $ compute test
Note that I had to add an instance of Computation for Double, which is just simply const. The test expression should be equivalent to (((2^2) * 5) * 0.5) + 100, and indeed comparing these two results I get the same value.
I'm not entirely sure this is the approach that you wanted, though. This also isn't really equivalent to the method shown in the link you posted, expressing variables would be pretty difficult with this encoding as there's no good way to feed in a map of all variable values to reduce the expression.
It depends on what you want to do with computations, but one idiomatic way is this:
data Computation = Computation { compute :: Double -> Double }
Then you can have:
idCmp :: Computation
idCmp = Computation id
squareCmp :: Computation
squareCmp = Computation (\i -> i * i)
composeCmp :: Computation -> Computation -> Computation
composeCmp b a = Computation (compute b . compute a)
scaleCmp :: Double -> Computation
scaleCmp r = Computation (r*)
etc. You could call this a kind of "computation combinators".

How to generate random triangle on a unit circle

I'm trying to find out the probability of the center of a unit circle falling inside a triangle. The vertexes of the triangle are randomly picked on the unit circle.
My idea is to pick a random float x in the range (-1.0, 1.0) and then randomly select up or down. This will get me one point (x, +/-sqrt(1 - x*x))
import Data.Random
main = do
x <- randomRIO (-1.0,1.0)
let y = (sample (randomElement [-1,1])) * sqrt(1-x*x)) -- I can't make this line work
So how do I select a random element from a [Int] ?
I don't speak Haskell, but to choose a point on a circle with even distribution, the variable you need to pick is the angle.
angle <- randomRIO(-pi, pi)
then
y = sin(angle)
x = cos(angle)
Pick 3 angles for your 3 dots and that gives you a random triangle.
As for testing if the center is in the triangle, I am fairly certain, though I have no demonstration so far, that you can go as follows:
Find the longest edge. The edge splits the circle in 2 parts
If the third vertex is in the smallest part, the center is not in the triangle
Else, it is
Edit, Demonstration
Let, ABC be a triangle in the circle of center D.
Let AB be the longest edge
Let C be on the largest half of the circle cut by AB
If D is not in ABC, then either AC or BC (for simplicity and since this is just naming convention, let assume AC) is the edge of the triangle that between AB and D
AC and AB being chords of the circle, the closest to the center is the longest
AC is longer than AB
How to apply this to angles only
From the previous demonstration, it quickly appears that if all 3 points are in the same half, the center is not in the triangle, otherwise it is.
The point is therefore to determine that the maximum distance between 2 points is pi.
Thanks to J. Abrahamson, we can compute the difference between all three points' angles, and sum the smaller two, return true if the sum is greater than pi.
The actual measurement of the distance between 2 points goes like this, as we need the absolute and most direct distance between the 2:
Compute the absolute distance diff = abs(theta1 - theta2)
If this is more than pi, use 2 * pi - diff
Look into the MonadRandom package, the uniform function will give you a random value from a list of values ([Int] in your case). I'm on my phone, but finding the package shouldn't be too tough. If you want to use a different distribution, look into random-fu.
An example of this using the MonadRandom package instead (cabal install MonadRandom):
import Control.Monad.Random
type R a = Rand StdGen a -- Just a type alias for less typing
type Point = (Double, Double)
type Triangle = (Point, Point, Point)
-- Monadic action
genAngle :: R Double
genAngle = getRandomR (-pi, pi)
-- Monadic action
genPoint :: R Point
genPoint = do
x <- genAngle
return (cos x, sin x)
-- Monadic action
genTriangle :: R Triangle
genTriangle = do
a <- genPoint
b <- genPoint
c <- genPoint
return (a, b, c)
-- Pure function
containsOrigin :: Triangle -> Bool
containsOrigin (a, b, c) = ??? -- You get to implement this (#njzk2 has given some pointers)
-- Monadic action
genTriangles :: R [Triangle]
genTriangles = do
tri <- genTriangle
rest <- genTriangles -- Recursion to get infinite list
return $ tri : rest
-- Monadic action
genTrianglesWithOrigin :: R [Triangle]
genTrianglesWithOrigin = do
triangles <- genTriangles
return $ filter containsOrigin triangles
main :: IO ()
main = do
triangles <- fmap (take 10) $ evalRandIO genTrianglesWithOrigin
mapM_ print triangles
If you want more reading on why MonadRandom is nicer than the base random package, I would recommend looking at this section of Learn You A Haskell. Basically, MonadRandom builds a nicer interface where not everything is in IO (which is dangerous, who knows what an IO function does) and provides some easy functions for generating random numbers, whereas the random package just provides the most barebones functionality possible. In fact, the Rand monad is nothing more than the State monad in disguise, it just keeps up with the state of your generator seed for you.

How do I model inheritance in Haskell?

I am attempting to create a game engine that is composed of a few different types:
data Camera = Camera ...
data Light = SpotLight ... | DirectionalLight ...
data Object = Monster ... | Player ... | NPC ...
However, I'm now trying to implement basic physics for all of these entities. This requires that they each contain a pos :: (Double, Double, Double) and a velocity :: (Double, Double, Double).
In object oriented languages, I would implement it as something like:
Camera implements PhysicalObject
where PhysicalObject contains the two properties pos and velocity.
My immediate reaction was to place them all in the same type:
data Object = Monster ... | Player ... | NPC ... | Camera ...
However, I fear that this might make it tough to implement camera-specific functions, light-specific functions, etc. In reality, they have very little else in common other than the fact that they all possess a physical position and velocity in the world.
Is there a simpler way to do this than defining the two properties inside each type constructor?
I can think of two approaches - type classes and lenses.
Type classes
class PhysicalObject m where
position :: m -> (Double, Double, Double)
velocity :: m -> (Double, Double, Double)
You would then make instances for the objects along the following lines
data Camera = Camera
{ cameraPosition :: (Double,Double,Double)
, cameraVelocity :: (Double,Double,Double)
}
instance PhysicalObject Camera where
position = cameraPosition
cameraVelocity = cameraVelocity
and similarly for your other types. Then any function which doesn't need to know the details of an object can just require its arguments to be instances of PhysicalObject, for example:
type TimeInterval = Double
newPosition :: PhysicalObject m => TimeInterval -> m -> (Double,Double,Double)
newPosition dt obj = (x + du * dt, y + dv * dt, z + dw * dt)
where
(x,y,z) = position obj
(u,v,w) = velocity obj
However, you will struggle to write functions which modify your objects using this code - the class tells Haskell how it can access the position and velocity of an object, but not how to modify them.
Lenses
The other option is to turn to the lens library. This is a bit of a beast to being with, but it allows you to write some very natural code. First, there's a bit of boilerplate
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
Now define some position and velocity data types. Don't worry about the weird field names prefixed with underscores - we won't be using them.
data Pos = Pos { _posX, _posY, _posZ :: Double }
data Vel = Vel { _velX, _velY, _velZ :: Double }
instance Show Pos where show (Pos x y z) = show (x,y,z)
instance Show Vel where show (Vel x y z) = show (x,y,z)
Now you use a bit of Template Haskell to derive lenses for your data types. This will generate type classes HasPos and HasVel whose methods allow you to access and modify any value that is an instance of those classes.
makeClassy ''Pos
makeClassy ''Vel
Now define your camera class, which includes a position and a velocity.
data Camera = Camera
{ _cameraPos :: Pos
, _cameraVel :: Vel } deriving (Show)
Another bit of Template Haskell will automatically create functions cameraPos and cameraVel that allow you to access and modify the position and velocity of your camera.
makeLenses ''Camera
Finally, declare that your camera is an instance of both the HasPos and HasVel classes, with a default implementation of their methods.
instance HasPos Camera where pos = cameraPos
instance HasVel Camera where vel = cameraVel
Now we're ready to do some real work. Let's define an example camera
camera = Camera (Pos 0 0 0) (Vel 10 5 0)
A function to modify the camera, returning a new one with an updated position, is
move :: (HasPos a, HasVel a) => TimeInterval -> a -> a
move dt obj = obj
& posX +~ dt * obj^.velX
& posY +~ dt * obj^.velY
& posZ +~ dt * obj^.velZ
Note that this is a completely generic function for moving any object that has a position and velocity - it's not at all specific to the Camera type. It also has the advantage of looking a lot like imperative code!
If you now load all this into GHCI, you can see it in action
>> camera
Camera {_cameraPos = (0.0,0.0,0.0), _cameraVel = (10.0,5.0,0.0)}
>> move 0.1 camera
Camera {_cameraPos = (1.0,0.5,0.0), _cameraVel = (10.0,5.0,0.0)}
I would implement it similar to:
type Position = (Double, Double, Double)
type Velocity = (Double, Double, Double)
class PhysicalObject a where
pos :: a -> Position
velocity :: a -> Velocity
data Camera = Camera
{ camPos :: Position
, camVel :: Velocity
} deriving (Eq, Show)
instance PhysicalObject Camera where
pos = camPos
velocity = camVel
Then you can do similarly for each type you define that needs PhysicalObject.
You'll want to begin to depend on things like typeclasses and object encodings. The first method is to encode the common interface as a typeclass each type inherits from.
class PhysicalObject o where
pos :: o -> Vector3
velocity :: o -> Vector3
The second is to build a common object
data PhysicalObject = PhysicalObject { poPos :: Vector3, poVelocity :: Vector3 }
data Monster = Monster { monsterPO :: PhysicalObject
, ... monsterStuff ...
}
which could even be used to instantiate the first typeclass
instance PhysicalObject PhysicalObject where
pos = poPos
velocity = poVelocity
instance PhysicalObject Monster where
pos = pos . monsterPO
velocity = velocity . monsterPO
Be careful with typeclass encodings like this, though, as too great a use of them often causes ambiguity when reading code. It can be difficult to understand the types and know which instance is being used.

Haskell space leak in implementation of BFS

I have been banging my head against a Haskell space leak (of the stack overflow kind, naturally) for a few straight days. It's frustrating because I'm attempting to mimic the BFS algorithm straight from CLR, which is not naturally recursive. NB: I have enabled BangPatterns and I have put a bang in front of every possible place where one can go, in an attempt to branch-and-bound this problem, with no effect. I have battled through space leaks before, and I am loth to give up and cry for help on this one, but at this point I'm stuck. I love coding in Haskell, and I understand the Zen of functional programming pretty well, but debugging space leaks is about as much fun as rolling around on a floor full of thumbtacks.
That said, my trouble appears to be a space leak of the typical "accumulator" kind. The stack evidently builds up around calls to bfs' in the code below. Any space-leak protips much appreciated.
import qualified Data.Map as M
import qualified Data.IntSet as IS
import qualified Data.Sequence as S
import qualified Data.List as DL
data BfsColor = White | Gray | Black deriving Show
data Node =
Node {
neighbors :: !IS.IntSet,
color :: !BfsColor,
depth :: !Int
}
type NodeID = Int
type NodeQueue = S.Seq NodeID
type Graph = M.Map NodeID Node
bfs :: Graph -> NodeID -> Graph
bfs graph start_node =
bfs' (S.singleton start_node) graph
bfs' :: NodeQueue -> Graph -> Graph
bfs' !queue !graph
| S.null queue = graph
| otherwise =
let (u,q1) = pop_left queue
Node children _ n = graph M.! u
(g2,q2) = IS.fold (enqueue_child_at_depth $ n+1) (graph,q1) children
g3 = set_color u Black g2
in bfs' q2 g3
enqueue_child_at_depth :: Int -> NodeID -> (Graph, NodeQueue)
-> (Graph, NodeQueue)
enqueue_child_at_depth depth child (graph,!queue) =
case get_color child graph of
White -> (set_color child Gray $ set_depth child depth graph,
queue S.|> child)
otherwise -> (graph,queue)
pop_left :: NodeQueue -> (NodeID, NodeQueue)
pop_left queue =
let (a,b) = S.splitAt 1 queue
in (a `S.index` 0, b)
set_color :: NodeID -> BfsColor -> Graph -> Graph
set_color node_id c graph =
M.adjust (\node -> node{color=c}) node_id graph
get_color :: NodeID -> Graph -> BfsColor
get_color node_id graph = color $ graph M.! node_id
set_depth :: NodeID -> Int -> Graph -> Graph
set_depth node_id d graph =
M.adjust (\node -> node{depth=d}) node_id graph
That looks much easier to understand. (You can still shrink the code by 1/2, though.)
Now, the nature of the space leak becomes apparent. Namely, the one thing that is never evaluated is the depth. It will pile up to a big expression 1+1+.... You can remove all the bang patterns and add a single one at
enqueue_child_at_depth !depth child (graph,queue)
to get rid of the space leak.
(Further code tips: You can replace the IS.IntSet by a simple list. The queue is best deconstructed and reconstructed along the lines of
go depth qs graph = case viewl qs of
EmptyL -> graph
q :< qs ->
let
qs' = (qs ><) . Seq.fromList
. filter (\q -> isWhite q graph)
. neighbors q $ graph
in ...
)
First of all, if would be very helpful if you could provide some simple test case (in the form of code) which demonstrates how this thing stack overflows.
Without it I, personally, can only speculate on the subject of reason for that.
As a speculation: is IS.fold strict enough? Well, for example the following simplest code stack overflows as well (GHC with -O2):
{-# LANGUAGE BangPatterns #-}
import qualified Data.IntSet as IS
test s = IS.fold it 1 s
where it !e !s = s+e
main = print $ test (IS.fromList [1..1000000])
The overflow problem with this code can be hackafixed (is there a better way?) like that:
test s = foldl' it 1 (IS.toList s)
where it !e !s = s+e
Maybe you want to look at IS.fold in your code as well.

Resources