This is a concept for a compositor. It would help me if you could review how well I expressed my ideas into the functional language.
I first divide everything in an areas that each window covers entirely or not:
Then I can walk the areas and render each corresponding window.
The actual composing of the vectors is a type hole right now because that's not really what this is about.
module SceneGraph where
import qualified Data.Vector as V
import Codec.Picture
import Data.Foldable
import Data.List
import Control.Applicative
import Data.Colour
import Data.Colour.SRGB
import Data.Colour.Names
data Point = Point { xC, yC :: Int}
--[upleft, downright)! upleft inclusive, downright exclusive
data Rectangle = Rectangle { upLeft, downRight :: Point}
--[upleft, downright]! inclusive
data Area = Area { upLeftA, downRightA :: Point}
data Window = Window
{ zIndex :: Int,
area :: Rectangle,
render :: V.Vector PixelRGB8 -> V.Vector PixelRGB8
}
getAllPoints :: [[Window]] -> [Point]
getAllPoints = foldMap (\z -> [upLeft (area z), downRight(area z)]) . fold
getXYCoordinatesSorted :: [Point] -> ([Int], [Int])
getXYCoordinatesSorted = sortPoints . foldr (\z y -> (xC z : fst y, yC z : snd y)) ([], [])
sortPoints :: ([Int], [Int]) -> ([Int], [Int])
sortPoints z = ((sort . nub . fst) z, (sort . nub . snd) z)
getBounds :: ([Int], [Int]) -> ([(Int, Int)], [(Int, Int)])
getBounds z = ((constructBounds . fst) z, (constructBounds . snd) z)
constructBounds :: [Int] -> [(Int, Int)]
constructBounds z = map (\i -> (fst i, snd i - 1)) $ zip z (tail z) ++ [(last z, last z + 1)]
boundsToAreas :: ([(Int, Int)], [(Int, Int)]) -> [Area]
boundsToAreas (xAxis, yAxis) = liftA2 boundToArea xAxis yAxis
boundToArea :: (Int, Int) -> (Int, Int) -> Area
boundToArea (xUp, xDown) (yLeft, yRight) = Area (Point xUp yLeft) (Point xDown yRight)
targetedWindows :: [[Window]] -> Point -> [Window]
targetedWindows windows point = (filter (\z -> isPointInsideRectangle (area z) point) . fold) windows
isPointInsideRectangle :: Rectangle -> Point -> Bool
isPointInsideRectangle rect point = (xC point >= xC (upLeft rect) && yC point <= yC (upLeft rect)) && (xC point >= xC (upLeft rect) && yC point <= yC (upLeft rect))
renderWindows :: [Area] -> [[Window]] -> V.Vector PixelRGB8
renderWindows areas window = foldr composeRenderings (createBackground (constructBackgroundArea areas)) (map (`renderWindow` window) areas)
constructBackgroundArea :: [Area] -> Area
constructBackgroundArea areas = Area ((upLeftA . head) areas) ((downRightA . last) areas)
renderWindow :: Area -> [[Window]] -> V.Vector PixelRGB8
renderWindow area windows = foldl (flip render) (createBackground area) (reverse (targetedWindows windows (upLeftA area)))
createBackground :: Area -> V.Vector PixelRGB8
createBackground area = _
composeRenderings :: V.Vector PixelRGB8 -> V.Vector PixelRGB8 -> V.Vector PixelRGB8
composeRenderings rendering background = _
lengthArea :: Area -> Int
lengthArea area = xC (downRightA area) - xC (upLeftA area)
heightArea :: Area -> Int
heightArea area = yC (upLeftA area) - yC (downRightA area)
All the functions combined would look like this:
composeWindows :: [[Window]] -> V.Vector PixelRGB8
composeWindows windows = renderWindows ((boundsToAreas . getBounds . getXYCoordinatesSorted . getAllPoints) windows) windows
The distinction between rectangles and areas is pretty ugly, but I don't know if my direction is even sound.