I am trying to reason how to convert an imperative style program into a functional one like Haskell.
The function is:
void calcPerim(point polycake[], int v, int y, double *perim1, double *perim2){
int next = 0;
int index = 0;
point points[2];
*perim1 = 0.0;
*perim2 = 0.0;
for(int i = 0; i < v; i++)
{
next = (i + 1) % v;
if(polycake[i].y < y && polycake[next].y < y)
(*perim1) += distance(polycake[i], polycake[next]);
else if(polycake[i].y > y && polycake[next].y > y)
(*perim2) += distance(polycake[i], polycake[next]);
else
{
points[index] = intersectPoint(polycake[i], polycake[next], y);
if(polycake[i].y < y)
{
(*perim1) += distance(polycake[i], points[index]);
(*perim2) += distance(polycake[next],points[index]);
}
else
{
(*perim2) += distance(polycake[i], points[index]);
(*perim1) += distance(polycake[next],points[index]);
}
index++;
}
}
(*perim1) += distance(points[0], points[1]);
(*perim2) += distance(points[0], points[1]);
}
I am finding it difficult to understand how I can turn this into a functional approach when it is updating two variables at the same time in some cases. Would it make sense when translating this into recursion to pass in a tuple (perim1, perim2)?
It might be a good idea to not translate it straight to Haskell but rather first to C++, which already allows to you structure it in a much more functional way.
First thing, as Cirdec commented, this function doesn't really take perim1 as arguments – those are “output arguments” as Fortran people would say, i.e. they're really results. Also, the v parameter seems to be basically just length of the input array. So in C++ you can reduce it to:
std::pair<double, double> calcPerim(std::vector <point> polycake, int y){
double perim1 = 0, perim2 = 0;
...
return std::make_pair(perim1, perim2);
}
Now, you have this mutating for loop. In a functional language, the general approach would be to replace that with recursion. For this, you need to make all mutable-state variables function parameters. That includes i, index, points and the perim accumulators (so they're back, in a way... but now as input arguments). You don't need next (which is anyways re-computed from scratch in each iteration).
std::pair<double, double> calcPerim_rec
( std::vector<point> polycake, int y
, int i, int index, std::array<point,2> points
, double perim1Acc, double perim2Acc ){
...
}
...to be used by
std::pair<double, double> calcPerim(std::vector<point> polycake, int y){
return calcPerim_rec(polycake, y, 0, 0, {}, 0, 0);
}
The recursive function looks very similar to your original loop body; you just need to phrase the end condition:
std::pair<double, double> calcPerim_rec
( std::vector<point> polycake, int y
, int i, int index, std::array<point,2> points
, double perim1Acc, double perim2Acc ){
if (i < polycake.length()) {
int next = (i + 1) % polycake.length();
if(polycake[i].y < y && polycake[next].y < y)
perim1Acc += distance(polycake[i], polycake[next]);
else if(polycake[i].y > y && polycake[next].y > y)
perim2Acc += distance(polycake[i], polycake[next]);
else
{
points[index] = intersectPoint(polycake[i], polycake[next], y);
if(polycake[i].y < y)
{
perim1Acc += distance(polycake[i], points[index]);
perim2Acc += distance(polycake[next],points[index]);
}
else
{
perim2Acc += distance(polycake[i], points[index]);
perim1Acc += distance(polycake[next],points[index]);
}
++index;
}
++i;
return calcPerim_rec
( polycake, y, i, index, points, perim1Acc, perim2Acc );
} else {
perim1Acc += distance(points[0], points[1]);
perim2Acc += distance(points[0], points[1]);
return std::make_pair(perim1Acc, perim2Acc);
}
}
There's still quite a bit of mutability going on, but we've already encapsulated it to happen all on local variables of the recursion function call, instead of variables lying around during the loop execution. And each of these variables is only updated once, followed by the recursive call, so you can just skip the mutation and simply pass a value plus update to the recursive call:
std::pair<double, double> calcPerim_rec
( std::vector<point> polycake, int y
, int i, int index, std::array<point,2> points
, double perim1Acc, double perim2Acc ){
if (i < polycake.length()) {
int next = (i + 1) % polycake.length();
if(polycake[i].y < y && polycake[next].y < y)
return calcPerim_rec
( polycake, y, i+1, index, points
, perim1Acc + distance(polycake[i], polycake[next])
, perim2Acc
);
else if(polycake[i].y > y && polycake[next].y > y)
return calcPerim_rec
( polycake, y, i+1, index, points
, perim1Acc
, perim2Acc + distance(polycake[i], polycake[next])
);
else
{
points[index] = intersectPoint(polycake[i], polycake[next], y);
if(polycake[i].y < y)
{
return calcPerim_rec
( polycake, y, i+1, index+1
, points
, perim1Acc + distance(polycake[i], points[index])
, perim2Acc + distance(polycake[next],points[index])
);
}
else
{
return calcPerim_rec
( polycake, y, i+1, index+1
, points
, perim1Acc + distance(polycake[i], points[index])
, perim2Acc + distance(polycake[next],points[index])
);
}
}
} else {
return std::make_pair( perim1Acc + distance(points[0], points[1])
, perim2Acc + distance(points[0], points[1]) );
}
}
Well, quite a bit of awkward passing-on of parameters, and we still have a mutation of points – but essentially, the code can now be translated to Haskell.
import Data.Vector (Vector, (!), length) as V
calcPerim_rec :: Vector Point -> Int -> Int -> Int -> Int -> [Point] -> (Double, Double) -> (Double, Double)
calcPerim_rec polycake y i index points (perim1Acc, perim2Acc)
= if i < V.length polycake
then
let next = (i + 1) `mod` V.length polycake
in if yCoord (polycake!i) < y && yCoord (polycake!next) < y
then calcPerim_rec polycake v y (i+1) index points
(perim1Acc + distance (polycake!i) (polycake!next)
perim2Acc
else
if yCoord (polycake!i) > y && yCoord (polycake!next) > y)
then calcPerim_rec polycake v y (i+1) index points
perim1Acc
(perim2Acc + distance (polycake!i) (polycake!next))
else
let points' = points ++ [intersectPoint (polycake!i) (polycake!next) y]
in if yCoord (polycake!i) < y
then calcPerim_rec polycake v y (i+1) (index+1)
points'
(perim1Acc + distance (polycake!i) (points!!index))
(perim2Acc + distance (polycake!next) (points!!index))
else calcPerim_rec polycake v y (i+1) (index+1)
points'
(perim1Acc + distance (polycake!i) points!!index))
(perim2Acc + distance (polycake!next) points!!index))
else ( perim1Acc + distance (points!!0) (points!!1)
, perim2Acc + distance (points!!0) (points!!1) )
There's a lot here that could be stylistically improved, but it should in essence work.
A good first thing to actually make it idiomatic is to try and get rid of indices. Indices are strongly eschewed in Haskell, and can often be avoided when you properly work with lists instead of arrays.
It's rarely a good idea to first write a C version and then try to translate it to Haskell.
Instead, consider what you're trying to do, rather than how you're trying to do it.
It appears that given a series of point representing a polygon
and a horizontal line at height y, you want to divide it into two polygons at line y and return the perimeter of both. The algorithm assumes the polygon is convex on the vertical axis:
You're doing this by:
Dividing the segments into those entirely over and entirely under y
Segments that cross y are split into two parts, the one above and the one below y, indicated by red dots.
Adding the intersection line between the two split points (cyan) to both polygons.
We can just implement that logic directly, rather than trying to emulate the iterative approach. Here's an example:
type Length = Double
type Point = (Double, Double)
type Segment = (Point, Point)
-- Check whether a segment is over, under or on the line given by y
segmentCompare :: Double -> Segment -> Ordering
segmentCompare y (p,q) =
case () of
_ | all (`isUnder` y) [p,q] -> LT
_ | all (`isOver` y) [p,q] -> GT
_ -> EQ
-- Partition a list into (lt, eq, gt) based on f
partition3 :: (Segment -> Ordering) -> [Segment] -> ([Segment], [Segment], [Segment])
partition3 f = p' ([], [], [])
where
p' (lt, eq, gt) (x:xs) =
case f x of
LT -> p' (x:lt, eq, gt) xs
EQ -> p' (lt, x:eq, gt) xs
GT -> p' (lt, eq, x:gt) xs
p' result [] = result
-- Split a crossing segment into an under part and over part, and return middle
divvy :: Double -> Segment -> (Segment, Segment, Point)
divvy y (start, end) =
if start `isUnder` y
then ((start, middle), (middle, end), middle)
else ((middle, end), (start, middle), middle)
where
middle = intersectPoint y (start, end)
-- Split a polygon in two, or Nothing if it's not convex enough
splitPolygon :: Double -> [Point] -> Maybe ([Segment], [Segment])
splitPolygon y list = do
let (under, crossing, over) = partition3 (segmentCompare y) pairs
case crossing of
-- No lines cross. Simple.
[] -> return (under, over)
-- Two segments cross. Divide them up.
[(p1,p2),(q1,q2)] ->
let (u1, o1, mid1) = divvy y (p1,p2)
(u2, o2, mid2) = divvy y (q1, q2)
split = (mid1, mid2) :: Segment
in return (split:u1:u2:under, split:o1:o2:over)
-- More segments cross. Algorithm doesn't work.
rest -> fail "Can't split polygons concave at y"
where
pairs = zip list (drop 1 $ cycle list) :: [Segment]
-- Your original function that sums the perimeter of both polygons
calcPerim :: Double -> [Point] -> Maybe (Length, Length)
calcPerim y list = do
(under, over) <- (splitPolygon y list :: Maybe ([Segment], [Segment]))
return (sumSegments under, sumSegments over)
-- Self explanatory helpers
distance :: Segment -> Length
distance ((ax, ay), (bx, by)) = sqrt $ (bx-ax)^2 + (by-ay)^2
intersectPoint :: Double -> Segment -> Point
intersectPoint y ((px, py), (qx, qy)) =
let slope = (qx-px)/(qy-py)
intercept = qy - slope*qx
x = (y - intercept)/slope
in
if slope /= 0
then (x,y)
else (px, y)
sumSegments :: [Segment] -> Length
sumSegments = sum . map distance
isUnder :: Point -> Double -> Bool
isUnder (_, py) y = py < y
isOver (_, py) y = py > y
You can give this a try, it is a direct translation of your C algorithm to Haskell
data Point = Point {x :: Float, y :: Float}
calcPerim :: [Point] -> Int -> Int -> (Float, Float)
calcPerim ls v some_y =
let (x:xs) = take v ls
r = zip (x:xs) (xs ++ [x])
(u, c, o, _) = foldl someFunction (0, 0, [], fromIntegral some_y :: Float) r
points_0 = last o
points_1 = o !! ((length o) - 2)
answer = (u + (distance points_0 points_1), c + (distance points_0 points_1))
in answer
someFunction :: (Float, Float, [Point], Float) -> (Point, Point) -> (Float, Float, [Point], Float)
someFunction (perim_1, perim_2, points, some_y) (i, nxt)
| y i < some_y && y nxt < some_y = (perim_1 + (distance i nxt), perim_2, points, some_y)
| y i > some_y && y nxt > some_y = (perim_1, perim_2 + (distance i nxt), points, some_y)
| y i < some_y = (perim_1 + (distance i temp_pt), perim_2 + (distance nxt temp_pt), temp_pt:points, some_y)
| otherwise = (perim_1 + (distance nxt temp_pt), perim_2 + (distance i temp_pt), temp_pt:points, some_y)
where temp_pt = intersection i nxt some_y
distance :: Point -> Point -> Float
distance p q = undefined
intersection :: Point -> Point -> Float -> Point
intersection p q f = undefined
I didn't run it. Not sure if I used the right fold.
Related
I have just tried rewriting some code, originally a short Javascript function, in Haskell. The original has 2 nested loops and the inner loop contains a check for equality against both loop counters:
function f(x, points){
var i, j;
var n = points.length;
var result = 0;
for(i=0; i<n; i++){
var xprod = 1;
for(j=0; j<n; j++){
if(j != i){
xprod *= (x - points[j][0]);
}
}
result += points[i][1] * xprod;
}
return result;
}
I was hoping to be able to simplify it in Haskell, but I couldn't figure out how get hold of the i and j values without effectively writing out every step of the original recursively. In Javascript Array.map passes the list position into the callback function as the second parameter, but it seems that map in Haskell doesn't do this. My current Haskell version looks awful to me as I'm passing in 2 copies of the array (one for each loop):
xproduct :: Int -> Int -> Double -> [(Double,Double)] -> Double
xproduct _ _ _ [] = 1
xproduct i j x (pt:todo)
| i == j = (xproduct i (j+1) x todo)
| otherwise = (xproduct i (j+1) x todo) * (x - (fst pt))
solvestep :: Int -> Double -> [(Double,Double)] -> [(Double,Double)] -> Double
solvestep _ _ _ [] = 0
solvestep i x pts (pt:todo) = ((snd pt) * xprod) + (solvestep (i+1) x pts todo)
where xprod = xproduct i 0 x pts
solve :: Double -> [(Double,Double)] -> Double
solve x points = solvestep 0 x points points
Is there a better way to do this?
I generally avoid using any indices at all, if possible. In this case, what you're really working with is: any one element of the list with all the other elements. No need to express that with index comparison, instead write a function that will give you a suitable look into the list:
pickouts :: [a] -> [(a,[a])]
pickouts [] = []
pickouts (x:xs) = (x,xs) : (second (x:) <$> pickouts xs)
Then, the actual computation becomes just
f :: Double -> [(Double,Double)] -> Double
f x points = sum [q * product [x-p | (p,_)<-ps] | ((_,q),ps) <- pickouts points]
I want to take two different images (taken from image files, like .png) and draw one over the other several times in different positions. The resulting image should be presented on screen or generate a new image file, whichever is easier. I´ll be taking that new image and drawing on it more with further operations
Is there any Haskell library that allows me to do this?
You can use JuicyPixels to do that sort of thing:
module Triangles where
import Codec.Picture
import LineGraphics
{-| Parameterize color smoothly as a function of angle -}
colorWheel :: Float -> Colour
colorWheel x = (r, g, b, a)
where
r = floor $ (cos x + 1) * (255 / 2)
g = floor $ (sin x + 1) * (255 / 2)
b = floor $ (cos (x+(pi/2)) + 1) * (255 / 2)
a = 255
{-| Draw a triangle centered about the point (x, y) -}
triangle :: Point -> Path
triangle (x, y) =
[ (x - k, y - k)
, (x + k, y - k)
, (x, y + k)
, (x - k, y - k)
]
where
size = 30
k = size / 2
{-|
Draw 'n' equally-spaced triangles at a radius of 'r' about a center
point, '(x, y)'.
-}
triangles :: Float -> Radius -> Vector -> Picture
triangles n r (x, y) =
[ (colorWheel theta, tri theta) | theta <- steps n ]
where
tri theta = triangle ((r * cos theta) + x, (r * sin theta) + y)
{-| Interpolate the range [0, 2pi] by 'n' steps -}
steps :: Float -> [Float]
steps n = map (\i -> i * (2*pi/n)) [0 .. n]
And we'll use this module of supporting code:
module LineGraphics (
Point, Vector, Line, Path, Picture, Colour, Radius,
black,
drawPicture,
) where
import Graphics.Rasterific hiding (Point, Vector, Line, Path, polygon)
import Graphics.Rasterific.Texture
import Codec.Picture
type Radius = Float
type Point = (Float, Float)
type Vector = (Float, Float)
type Line = (Point, Point)
type Path = [Point]
type Picture = [(Colour, Path)]
type Colour = (Int, Int, Int, Int) -- red, green, blue, opacity
black = (0, 0, 0, 255)
drawPicture :: Float -> Picture -> Image PixelRGBA8
drawPicture linewidth picture =
renderDrawing 800 800 (toColour black) $
mapM_ renderFn picture
where
renderFn (col, path) = withTexture (uniformTexture $ toColour col) (drawPath path)
drawPath points = stroke linewidth JoinRound (CapRound, CapStraight 0) $
polyline (map (\(x, y) -> V2 x y) points)
toColour (a,b,c,d) = PixelRGBA8
(fromIntegral a) (fromIntegral b) (fromIntegral c) (fromIntegral d)
And here's what we get:
type Coordinate = (XCoord, YCoord)
type XCoord = Coord
type YCoord = Coord
type Coord = Integer
coordInBound :: Coordinate -> Bool
coordInBound (XCoord, YCoord) =
XCoord
|x >= 0 && x <= 9 = True
|otherwise = False
YCoord
|y >= 0 && y <= 9 = True
|otherwise = False
I'm Trying to write a function that returns True if the coordinates are in a 10 by 10 grid from 0,0 -> 9,9
Try breaking the problem into two pieces: Is the x coordinate in bounds? Is the y coordinate in bounds? And combining those together to figure out if they're both in bounds.
xInBounds :: XCoord -> Bool
xInBounds x | x >= 0 && x <= 9 = True
| otherwise = False
yInBounds :: YCoord -> Bool
???
What you have right now won't compile because, among other things, the names of variables in patterns (XCoord, YCoord) can't start with capital letters. Names that start with capital letters in haskell are reserved for types like Coordinate and constructors like True. Variables have lower case names like coordInBound.
With xInBounds and yInBounds try to complete coordInBound using lower case variable names
coordInBound :: Coordinate -> Bool
coordInBound (x, y) = ???
Since XCoord and YCoord are just integers, you can simply check if both are in range of [0, 9]:
type Coordinate = (XCoord, YCoord)
type XCoord = Coord
type YCoord = Coord
type Coord = Integer
coordInBound :: Coordinate -> Bool
coordInBound (x, y) =
x >= 0 && x <= 9 && y >= 0 && y <= 9
main = print $ coordInBound (9, 0)
But if you wish XCoord and YCoord were types, then you'd need the following type definition and usage:
data XCoord = XCoord Int
data YCoord = YCoord Int
type Coordinate = (XCoord, YCoord)
coordInBound :: Coordinate -> Bool
coordInBound (XCoord x, YCoord y) =
x >= 0 && x <= 9 && y >= 0 && y <= 9
main = print $ coordInBound (XCoord 9, YCoord 0)
The output looks like this:
You should just see a flat, continuous red wall on one side, blue wall on another, green on another, yellow on another (see the definition of the map, testMapTiles, it's just a map with four walls). Yet there are these phantom wall faces of varying height, which are perpendicular to the real walls. Why?
Note that the white "gaps" aren't actually gaps: it's trying to draw a wall of height Infinity (distance 0). If you specifically account for it (this version of the code doesn't) and just cap it at screen height, then you just see a very high wall there.
The source code is below. It's plain Haskell, using Haste to compile to JavaScript and render to canvas. It is based on the C++ code from this tutorial, though note that I replaced mapX and mapY with tileX and tileY, and I don't have the ray prefix for pos and dir within the main loop. Any discrepancies from the C++ code are probably what's breaking everything, but I can't seem to find any after having pored over this code many times.
Any help?
import Data.Array.IArray
import Control.Arrow (first, second)
import Control.Monad (forM_)
import Haste
import Haste.Graphics.Canvas
data MapTile = Empty | RedWall | BlueWall | GreenWall | YellowWall deriving (Eq)
type TilemapArray = Array (Int, Int) MapTile
emptyTilemapArray :: (Int, Int) -> TilemapArray
emptyTilemapArray dim#(w, h) = listArray ((1, 1), dim) $ replicate (w * h) Empty
testMapTiles :: TilemapArray
testMapTiles =
let arr = emptyTilemapArray (16, 16)
myBounds#((xB, yB), (w, h)) = bounds arr
in listArray myBounds $ flip map (indices arr) (\(x, y) ->
if x == xB then RedWall
else if y == yB then BlueWall
else if x == w then GreenWall
else if y == h then YellowWall
else Empty)
type Vec2 a = (a, a)
type DblVec2 = Vec2 Double
type IntVec2 = Vec2 Int
add :: (Num a) => Vec2 a -> Vec2 a -> Vec2 a
add (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)
mul :: (Num a) => Vec2 a -> a -> Vec2 a
mul (x, y) factor = (x * factor, y * factor)
rot :: (Floating a) => Vec2 a -> a -> Vec2 a
rot (x, y) angle =
(x * (cos angle) - y * (sin angle), x * (sin angle) + y * (cos angle))
dbl :: Int -> Double
dbl = fromIntegral
-- fractional part of a float
-- `truncate` matches behaviour of C++'s int()
frac :: Double -> Double
frac d = d - dbl (truncate d)
-- get whole and fractional parts of a float
split :: Double -> (Int, Double)
split d = (truncate d, frac d)
-- stops 'Warning: Defaulting the following constraint(s) to type ‘Integer’'
square :: Double -> Double
square = (^ (2 :: Int))
-- raycasting algorithm based on code here:
-- http://lodev.org/cgtutor/raycasting.html#Untextured_Raycaster_
data HitSide = NorthSouth | EastWest deriving (Show)
-- direction, tile, distance
type HitInfo = (HitSide, IntVec2, Double)
-- pos: start position
-- dir: initial direction
-- plane: camera "plane" (a line, really, perpendicular to the direction)
traceRays :: TilemapArray -> Int -> DblVec2 -> DblVec2 -> DblVec2 -> [HitInfo]
traceRays arr numRays pos dir plane =
flip map [0..numRays] $ \x ->
let cameraX = 2 * ((dbl x) / (dbl numRays)) - 1
in traceRay arr pos $ dir `add` (plane `mul` cameraX)
traceRay :: TilemapArray -> DblVec2 -> DblVec2 -> HitInfo
traceRay arr pos#(posX, posY) dir#(dirX, dirY) =
-- map tile we're in (whole part of position)
-- position within map tile (fractional part of position)
let ((tileX, fracX), (tileY, fracY)) = (split posX, split posY)
tile = (tileX, tileY)
-- length of ray from one x or y-side to next x or y-side
deltaDistX = sqrt $ 1 + (square dirY / square dirX)
deltaDistY = sqrt $ 1 + (square dirX / square dirY)
deltaDist = (deltaDistX, deltaDistY)
-- direction of step
stepX = if dirX < 0 then -1 else 1
stepY = if dirY < 0 then -1 else 1
step = (stepX, stepY)
-- length of ray from current position to next x or y-side
sideDistX = deltaDistX * if dirX < 0 then fracX else 1 - fracX
sideDistY = deltaDistY * if dirY < 0 then fracY else 1 - fracY
sideDist = (sideDistX, sideDistY)
(hitSide, wallTile) = traceRayInner arr step deltaDist tile sideDist
in (hitSide, wallTile, calculateDistance hitSide pos dir wallTile step)
traceRayInner :: TilemapArray -> IntVec2 -> DblVec2 -> IntVec2 -> DblVec2 -> (HitSide, IntVec2)
traceRayInner arr step#(stepX, stepY) deltaDist#(deltaDistX, deltaDistY) tile sideDist#(sideDistX, sideDistY)
-- a wall has been hit, report hit direction and coördinates
| arr ! tile /= Empty = (hitSide, tile)
-- advance until a wall is hit
| otherwise = case hitSide of
EastWest ->
let newSideDist = first (deltaDistX+) sideDist
newTile = first (stepX+) tile
in
traceRayInner arr step deltaDist newTile newSideDist
NorthSouth ->
let newSideDist = second (deltaDistY+) sideDist
newTile = second (stepY+) tile
in
traceRayInner arr step deltaDist newTile newSideDist
where
hitSide = if sideDistX < sideDistY then EastWest else NorthSouth
-- calculate distance projected on camera direction
-- (an oblique distance would give a fisheye effect)
calculateDistance :: HitSide -> DblVec2 -> DblVec2 -> IntVec2 -> IntVec2 -> Double
calculateDistance EastWest (startX, _) (dirX, _) (tileX, _) (stepX, _) =
((dbl tileX) - startX + (1 - dbl stepX) / 2) / dirX
calculateDistance NorthSouth (_, startY) (_, dirY) (_, tileY) (_, stepY) =
((dbl tileY) - startY + (1 - dbl stepY) / 2) / dirY
-- calculate the height of the vertical line on-screen based on the distance
calculateHeight :: Double -> Double -> Double
calculateHeight screenHeight 0 = screenHeight
calculateHeight screenHeight perpWallDist = screenHeight / perpWallDist
width :: Double
height :: Double
(width, height) = (640, 480)
main :: IO ()
main = do
cvElem <- newElem "canvas" `with` [
attr "width" =: show width,
attr "height" =: show height
]
addChild cvElem documentBody
Just canvas <- getCanvas cvElem
let pos = (8, 8)
dir = (-1, 0)
plane = (0, 0.66)
renderGame canvas pos dir plane
renderGame :: Canvas -> DblVec2 -> DblVec2 -> DblVec2 -> IO ()
renderGame canvas pos dir plane = do
let rays = traceRays testMapTiles (floor width) pos dir plane
render canvas $ forM_ (zip [0..width - 1] rays) (\(x, (side, tile, dist)) ->
let lineHeight = calculateHeight height dist
wallColor = case testMapTiles ! tile of
RedWall -> RGB 255 0 0
BlueWall -> RGB 0 255 0
GreenWall -> RGB 0 0 255
YellowWall -> RGB 255 255 0
_ -> RGB 255 255 255
shadedWallColor = case side of
EastWest ->
let (RGB r g b) = wallColor
in RGB (r `div` 2) (g `div` 2) (b `div` 2)
NorthSouth -> wallColor
in color shadedWallColor $ do
translate (x, height / 2) $ stroke $ do
line (0, -lineHeight / 2) (0, lineHeight / 2))
-- 25fps
let fps = 25
timeout = (1000 `div` fps) :: Int
rots_per_min = 1
rots_per_sec = dbl rots_per_min / 60
rots_per_frame = rots_per_sec / dbl fps
tau = 2 * pi
increment = tau * rots_per_frame
setTimeout timeout $ do
renderGame canvas pos (rot dir $ -increment) (rot plane $ -increment)
HTML page:
<!doctype html>
<meta charset=utf-8>
<title>Raycaster</title>
<noscript>If you're seeing this message, either your browser doesn't support JavaScript, or it is disabled for some reason. This game requires JavaScript to play, so you'll need to make sure you're using a browser which supports it, and enable it, to play.</noscript>
<script src=raycast.js></script>
The "phantom faces" are occurring because an incorrect HitSide is being reported: you're saying the face was hit on a horizontal move (EastWest), but was actually hit on a vertical move (NorthSouth), or vice-versa.
Why is it reporting an incorrect value, then? if sideDistX < sideDistY then EastWest else NorthSouth seems pretty foolproof, right? And it is.
The problem isn't how we calculated that value. It's when we calculated that value. The distance calculation function needs to know the direction we moved in to get to the wall. However, what we've actually given is the direction we would move in if we were to keep going (that is, if that tile wasn't a wall, or we were to ignore it for some reason).
Look at the Haskell code:
traceRayInner arr step#(stepX, stepY) deltaDist#(deltaDistX, deltaDistY) tile sideDist#(sideDistX, sideDistY)
-- a wall has been hit, report hit direction and coördinates
| arr ! tile /= Empty = (hitSide, tile)
-- advance until a wall is hit
| otherwise = case hitSide of
EastWest ->
let newSideDist = first (deltaDistX+) sideDist
newTile = first (stepX+) tile
in
traceRayInner arr step deltaDist newTile newSideDist
NorthSouth ->
let newSideDist = second (deltaDistY+) sideDist
newTile = second (stepY+) tile
in
traceRayInner arr step deltaDist newTile newSideDist
where
hitSide = if sideDistX < sideDistY then EastWest else NorthSouth
Notice that we do things in this order:
calculate hitSide
check if a wall has been hit, and if so, report hitSide
move
Compare this to the original C++ code:
//perform DDA
while (hit == 0)
{
//jump to next map square, OR in x-direction, OR in y-direction
if (sideDistX < sideDistY)
{
sideDistX += deltaDistX;
mapX += stepX;
side = 0;
}
else
{
sideDistY += deltaDistY;
mapY += stepY;
side = 1;
}
//Check if ray has hit a wall
if (worldMap[mapX][mapY] > 0) hit = 1;
}
It does things in a different order:
check if a wall has been hit, and if so, report side (equivalent to hitSide)
move and calculate side
The C++ code only calculates side when it moves, and then it reports that value if it hits a wall. So, it reports the way it moved in order to hit the wall.
The Haskell code calculates side whether or not it moves: so it's correct for each move, but when it hits a wall, it reports the way it would have moved were it to keep going.
So, the Haskell code can be fixed by re-ordering it so that it checks for a hit after moving, and if so, reports the hitSide value from that move. This isn't pretty code, but it works:
traceRayInner arr step#(stepX, stepY) deltaDist#(deltaDistX, deltaDistY) tile sideDist#(sideDistX, sideDistY) =
let hitSide = if sideDistX < sideDistY then EastWest else NorthSouth
in case hitSide of
EastWest ->
let newSideDist = first (deltaDistX+) sideDist
newTile = first (stepX+) tile
in case arr ! newTile of
-- advance until a wall is hit
Empty -> traceRayInner arr step deltaDist newTile newSideDist
-- a wall has been hit, report hit direction and coördinates
_ -> (hitSide, newTile)
NorthSouth ->
let newSideDist = second (deltaDistY+) sideDist
newTile = second (stepY+) tile
in case arr ! newTile of
-- advance until a wall is hit
Empty -> traceRayInner arr step deltaDist newTile newSideDist
-- a wall has been hit, report hit direction and coördinates
_ -> (hitSide, newTile)
Problem solved!
Side note: I figured out what was wrong after carrying out the algorithm on paper. While in that particular case it just so happened the last two HitSide values matched, it became obvious that that they might not in every case. So, a big thanks to Madsy on Freenode's #algorithms for suggesting trying it out on paper. :)
I have the following in Java which basically does a nested triangular loop:
int n = 10;
B bs[] = new B[n];
// some initial values, bla bla
double dt = 0.001;
for (int i = 0; i < n; i++) {
bs[i] = new B();
bs[i].x = i * 0.5;
bs[i].v = i * 2.5;
bs[i].m = i * 5.5;
}
for (int i = 0; i < n; i++) {
for (int j = **(i+1)**; j < n; j++) {
double d = bs[i].x - bs[j].x;
double sqr = d * d + 0.01;
double dist = Math.sqrt(sqr);
double mag = dt / (sqr * dist);
bs[i].v -= d * bs[j].m * mag;
**bs[j].v += d * bs[i].m * mag;**
}
}
// printing out the value v
for (int i = 0; i < n; i++) {
System.out.println(bs[i].v);
}
Class B:
class B {
double x, v, m;
}
In each iteration, the value at index i and j of the array is updated at the same time thus avoiding to do a complete nested loop. The following gives the same result but it does a complete nested loop (excuse me for the terms i'm using, they may not be correct but i hope it does make sense).
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
double d = bs[i].x - bs[j].x;
double sqr = d * d + 0.01;
double dist = Math.sqrt(sqr);
double mag = dt / (sqr * dist);
bs[i].v -= d * bs[j].m * mag;
}
}
NOTE:
the only change from the previous code is int j = 0; NOT int j = (i+1); and removed bs[j].v += d * bs[i].m * mag;
I want to do same in Haskell but having difficulty to think about it properly. I have the following code. The array in the Haskell version is represented as a list (xs) which i've initialised to 0.
n = 20
xs = replicate n 0
update = foldl' (update') xs [0..(n-1)]
where
update' i = update'' i (i+1) []
update'' i j acc
| j == n = acc
| otherwise = new_acc
where
new_acc = result:acc
result = ...do something
I am going to have very big value for n e.g. 1000, 5000, etc.
A complete nested loop when n = 1000 gives length [(i,j)|i<-[0..1000],j<-[0..1000]] = 1002001 but a triangular version gives length [(i,j)|i<-[0..1000],j<-[(i+1)..1000]]
= 500500. Doing 2 maps in Haskell is easy to get it to do the complete loops but I want the triangular version. I guess this implies keeping the changes to i and j in a list and then update the original list at the end? Any idea would be much appreciated. Thanks
Here's a straightforward translation using unboxed mutable vectors from the vector package. Code is somewhat ugly, but should be very fast:
module Main
where
import qualified Data.Vector.Unboxed as U
import qualified Data.Vector.Unboxed.Mutable as M
numElts :: Int
numElts = 10
dt :: Double
dt = 0.001
loop :: Int -> M.IOVector Double -> M.IOVector Double
-> M.IOVector Double -> IO ()
loop n x v m = go 0
where
doWork i j = do xI <- M.read x i
xJ <- M.read x j
vI <- M.read v i
vJ <- M.read v j
mI <- M.read m i
mJ <- M.read m j
let d = xI - xJ
let sqr = d * d + 0.01
let dist = sqrt sqr
let mag = dt / (sqr * dist)
M.write v i (vI - d * mJ * mag)
M.write v j (vJ + d * mI * mag)
go i | i < n = do go' (i+1)
go (i+1)
| otherwise = return ()
where
go' j | j < n = do doWork i j
go' (j + 1)
| otherwise = return ()
main :: IO ()
main = do x <- generateVector 0.5
v <- generateVector 2.5
m <- generateVector 5.5
loop numElts x v m
v' <- U.unsafeFreeze v
U.forM_ v' print
where
generateVector :: Double -> IO (M.IOVector Double)
generateVector d = do v <- M.new numElts
generateVector' numElts d v
return v
generateVector' :: Int -> Double -> M.IOVector Double -> IO ()
generateVector' n d v = go 0
where
go i | i < n = do M.unsafeWrite v i (fromIntegral i * d)
go (i+1)
| otherwise = return ()
Update: Regarding the "very fast" claim: I benchmarked my solution against the pure one provided by Federico and got the following results (for n = 1000):
benchmarking pureSolution
collecting 100 samples, 1 iterations each, in estimated 334.5483 s
mean: 2.949640 s, lb 2.867693 s, ub 3.005429 s, ci 0.950
std dev: 421.1978 ms, lb 343.8233 ms, ub 539.4906 ms, ci 0.950
found 4 outliers among 100 samples (4.0%)
3 (3.0%) high severe
variance introduced by outliers: 5.997%
variance is slightly inflated by outliers
benchmarking pureVectorSolution
collecting 100 samples, 1 iterations each, in estimated 280.4593 s
mean: 2.747359 s, lb 2.709507 s, ub 2.803392 s, ci 0.950
std dev: 237.7489 ms, lb 179.3110 ms, ub 311.8813 ms, ci 0.950
found 13 outliers among 100 samples (13.0%)
7 (7.0%) high mild
6 (6.0%) high severe
variance introduced by outliers: 2.998%
variance is slightly inflated by outliers
benchmarking imperativeSolution
collecting 100 samples, 1 iterations each, in estimated 5.905104 s
mean: 58.59154 ms, lb 56.79405 ms, ub 60.60033 ms, ci 0.950
std dev: 11.70101 ms, lb 9.120100 ms, ub NaN s, ci 0.950
So the imperative solution is approx. 50 times faster than the functional one (the difference is even more dramatic for smaller n, when everything fits in cache). I tried to make Federico's solution work with unboxed vectors, but apparently it relies on laziness in a crucial way, which makes the unboxed version loop forever. The "pure vector" version uses boxed vectors.
I'm not sure this solves your problem because I didn't grasp it completely yet, but the triangular loop itself is very easy to do in Haskell:
triangularLoop :: (a -> a -> b) -> [a] -> [b]
triangularLoop f xs = do
(x1 : t) <- tails xs
x2 <- t
return $ f x1 x2
Or, written without the monadic syntax,
triangularLoop f = concat . map singlePass . tails
where
singlePass [] = []
singlePass (h:t) = map (f h) t
A typical, idiomatic way of writing nested loops in Haskell is using list comprehensions.
Here is how I would translate your code:
import Data.Array
import Data.List (tails)
data Body = Body {x::Double,v::Double,m::Double}
deriving Show
n::Int
n = 9
dt::Double
dt = 0.001
bs_0 :: Array Int Body
bs_0 = array (0,n) [(i,Body {x = i'*0.5,v = i'*2.5,m = i'*5.5}) |
i <- [0..n], let i' = fromIntegral i]
bs :: Array Int Body
bs = accum (\b dv -> b {v = v b + dv}) bs_0 dvs
where
dvs :: [(Int,Double)]
dvs = concat [[(i,dv_i),(j,dv_j)] | (i:is) <- tails [0..n],
j <- is,
let d = x(bs!i) - x(bs!j)
sqr = d * d + 0.01
dist = sqrt sqr
mag = dt / (sqr * dist)
dv_i = -d * m(bs!j) * mag
dv_j = d * m(bs!i) * mag]
main :: IO()
main = mapM_ print (assocs bs)