```{-# OPTIONS_HADDOCK hide #-}

{-# LANGUAGE NoImplicitPrelude #-}

module Imj.Geo.Discrete.Bresenham3
( bresenham3Length
, bresenham3
) where

import           Imj.Prelude

import           Data.List( zip3 )

--
-- slightly modified to fix a bug when rise1 == rise2, rise1 > run and rise2 > run
bres :: Int -> Int -> Int -> [(Int, Int, Int)]
bres run rise1 rise2
| run < 0  =   [(-x,  y,  z) | (x, y, z) <- bres (-run) rise1 rise2]
| rise1 < 0  = [( x, -y,  z) | (x, y, z) <- bres run (-rise1) rise2]
| rise2 < 0  = [( x,  y, -z) | (x, y, z) <- bres run rise1 (-rise2)]
| rise1 > max run rise2 =
[( x, y, z) | (y, x, z) <- bres rise1 run rise2]
| rise2 > max run rise1 =
[( x, y, z) | (z, x, y) <- bres rise2 run rise1]
| run < rise1 =
[( x, y, z) | (y, x, z) <- bres rise1 run (assert (rise1 == rise2) rise2)]
| otherwise = zip3 [0..run]
(map fst \$ iterate (step rise1) (0, run `div` 2))
(map fst \$ iterate (step rise2) (0, run `div` 2))
where
step rise (y, err)
| err' < 0 = (y + 1, err' + run)
| otherwise  = (y, err')
where err' = err - rise

-- | 3D version of the bresenham algorithm.
{-# INLINABLE bresenham3 #-}
bresenham3 :: (Int, Int, Int) -> (Int, Int, Int) -> [(Int, Int, Int)]
bresenham3 (x1, y1, z1) (x2, y2, z2) =
[(x1+x, y1+y, z1+z) | (x, y, z) <- bres (x2-x1) (y2-y1) (z2-z1)]

-- | Returns the 3D bresenham length between two 3D coordinates.
{-# INLINABLE bresenham3Length #-}
-- avoid using unsigned types, as it complicates the calculations
bresenham3Length :: (Int, Int, Int) -> (Int, Int, Int) -> Int
bresenham3Length (x1, y1, z1) (x2, y2, z2)
= succ \$ max (abs (x1-x2)) \$ max (abs (y1-y2)) (abs (z1-z2))
```