shamansir / bin-pack / BinPack

Simple bin packing for rectangles.

Based on the version in Haskell.

[The demo of a similar algorithm](https://observablehq.com/

Core type


type BinPack a

BinPack a, where a is the type of what every rectangle is associated with (what lies in every cell). For example, it could be Color.

Bounds


type alias Bounds =
{ x : Basics.Float
, y : Basics.Float
, width : Basics.Float
, height : Basics.Float 
}

The bounds, top left corner and width/height.

Create container

container : Basics.Float -> Basics.Float -> BinPack a

Create an empty container with given height and width.

Packing

pack : ( { width : Basics.Float, height : Basics.Float }, a ) -> BinPack a -> Maybe (BinPack a)

Try to pack the value in a rectangle with given width and height. If the rect doesn't fit, Nothing is returned.

carelessPack : ( { width : Basics.Float, height : Basics.Float }, a ) -> BinPack a -> BinPack a

Try to pack the value in a rectangle with given width and height. If the rectangle doesn't fit, ignore that fact and return previous condition of BinPack.

packAll : Basics.Float -> Basics.Float -> List ( { width : Basics.Float, height : Basics.Float }, a ) -> BinPack a

Try to pack all the values with given dimensions in a BinPack container with given width and height, ignore the item when it doesn't fit.

packAllIn : List ( { width : Basics.Float, height : Basics.Float }, a ) -> BinPack a -> BinPack a

Try to pack all the values with given dimensions in the given BinPack container.

Search

find : { x : Basics.Float, y : Basics.Float } -> BinPack a -> Maybe ( a, Bounds )

Try to find a value in a structure using given coordinates.

Folding

fold : (a -> b -> b) -> b -> BinPack a -> b

Fold the BinPack a to any other type, for example:

BinPack.container 300 250
    |> carelessPack ( { width = 10, height = 30 }, Color.black )
    |> carelessPack ( { width = 20, height = 15 }, Color.red )
    |> carelessPack ( { width = 5, height = 25 }, Color.blue )
    |> fold (::) []

-- == [ Color.black, Color.red, Color.blue ]

foldWithFreeSpace : (Maybe a -> b -> b) -> b -> BinPack a -> b

Fold the BinPack using the information about if it's a free space (Nothing) or a node (Maybe a).

foldGeometry : (( a, Bounds ) -> k -> k) -> k -> BinPack a -> k

Fold the structure, using both the values and their bounds:

BinPack.container 20 100
    |> carelessPack ( { width = 10, height = 30 }, Color.black )
    |> carelessPack ( { width = 20, height = 15 }, Color.red )
    |> carelessPack ( { width = 5, height = 25 }, Color.blue )
    |> carelessPack ( { width = 12, height = 25 }, Color.green )
    |> foldGeometry (::) []

-- ==
--    [ ( Color.black, { x = 0, y = 0, width = 10, height = 30 } )
--    , ( Color.red, { x = 0, y = 30, width = 20, height = 15 } )
--    , ( Color.blue, { x = 10, y = 0, width = 5, height = 25 } )
--    , ( Color.green, { x = 0, y = 45, width = 12, height = 25 } )
--    ]

foldGeometryWithFreeSpace : (( Maybe a, Bounds ) -> k -> k) -> k -> BinPack a -> k

Fold with the information if it's a free space (Nothing) or a node (Just a), including bounds.

Lists

toList : BinPack a -> List ( a, Bounds )

Convert the structure to the list of values and their bounds

toListWithFreeSpace : BinPack a -> List ( Maybe a, Bounds )

Convert the structure to the list of values and their bounds + information if it's a free space or a node.

Mapping

map : (a -> b) -> BinPack a -> BinPack b

Substitute all of the cells with different ones, using the previous ones as the source.