{"id":16533492,"url":"https://github.com/chinedufn/rectangle-pack","last_synced_at":"2025-04-05T11:13:10.101Z","repository":{"id":38891619,"uuid":"245916211","full_name":"chinedufn/rectangle-pack","owner":"chinedufn","description":"A general purpose, deterministic bin packer designed to conform to any two or three dimensional use case.","archived":false,"fork":false,"pushed_at":"2024-08-28T14:01:38.000Z","size":193,"stargazers_count":72,"open_issues_count":0,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-10-18T07:11:15.139Z","etag":null,"topics":["2d","3d","atlas","bin","pack","rect","rectangle","texture"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chinedufn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-03-09T01:14:51.000Z","updated_at":"2024-08-28T14:01:42.000Z","dependencies_parsed_at":"2024-10-20T19:48:28.629Z","dependency_job_id":null,"html_url":"https://github.com/chinedufn/rectangle-pack","commit_stats":{"total_commits":45,"total_committers":2,"mean_commits":22.5,"dds":"0.022222222222222254","last_synced_commit":"99774a40a2cba9d380614ad87b463a981c6b1cee"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinedufn%2Frectangle-pack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinedufn%2Frectangle-pack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinedufn%2Frectangle-pack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinedufn%2Frectangle-pack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chinedufn","download_url":"https://codeload.github.com/chinedufn/rectangle-pack/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247325696,"owners_count":20920714,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["2d","3d","atlas","bin","pack","rect","rectangle","texture"],"created_at":"2024-10-11T18:14:58.073Z","updated_at":"2025-04-05T11:13:10.082Z","avatar_url":"https://github.com/chinedufn.png","language":"Rust","readme":"# rectangle-pack [![Actions Status](https://github.com/chinedufn/rectangle-pack/workflows/test/badge.svg)](https://github.com/chinedufn/rectangle-pack/actions) [![docs](https://docs.rs/rectangle-pack/badge.svg)](https://docs.rs/rectangle-pack)\n\n\u003e A general purpose, deterministic bin packer designed to conform to any two or three dimensional use case.\n\n`rectangle-pack` is a library focused on laying out any number of smaller rectangles (both 2d rectangles and 3d rectangular prisms) inside any number of larger rectangles.\n\n`rectangle-pack` exposes an API that gives the consumer control over how rectangles are packed - allowing them to tailor\nthe packing to their specific use case.\n\nWhile `rectangle-pack` was originally designed with texture atlas related use cases in mind - **the library itself has no notions of images and can be used\nin any rectangle packing context**.\n\n## Quickstart\n\n```\n# In your Cargo.toml\nrectangle-pack = \"0.4\"\n```\n\n```rust\n//! A basic example of packing rectangles into target bins\n\nuse rectangle_pack::{\n    GroupedRectsToPlace,\n    RectToInsert,\n    pack_rects,\n    TargetBin,\n    volume_heuristic,\n    contains_smallest_box\n};\nuse std::collections::BTreeMap;\n\n// A rectangle ID just needs to meet these trait bounds (ideally also Copy).\n// So you could use a String, PathBuf, or any other type that meets these\n// trat bounds. You do not have to use a custom enum.\n#[derive(Debug, Hash, PartialEq, Eq, Clone, Ord, PartialOrd)]\nenum MyCustomRectId {\n    RectOne,\n    RectTwo,\n    RectThree,\n}\n\n// A target bin ID just needs to meet these trait bounds (ideally also Copy)\n// So you could use a u32, \u0026str, or any other type that meets these\n// trait bounds. You do not have to use a custom enum.\n#[derive(Debug, Hash, PartialEq, Eq, Clone, Ord, PartialOrd)]\nenum MyCustomBinId {\n    DestinationBinOne,\n    DestinationBinTwo,\n}\n\n// A placement group just needs to meet these trait bounds (ideally also Copy).\n//\n// Groups allow you to ensure that a set of rectangles will be placed\n// into the same bin. If this isn't possible an error is returned.\n//\n// Groups are optional.\n//\n// You could use an i32, \u0026'static str, or any other type that meets these\n// trat bounds. You do not have to use a custom enum.\n#[derive(Debug, Hash, PartialEq, Eq, Clone, Ord, PartialOrd)]\nenum MyCustomGroupId {\n    GroupIdOne\n}\n\nlet mut rects_to_place = GroupedRectsToPlace::new();\nrects_to_place.push_rect(\n    MyCustomRectId::RectOne,\n    Some(vec![MyCustomGroupId::GroupIdOne]),\n    RectToInsert::new(10, 20, 255)\n);\nrects_to_place.push_rect(\n    MyCustomRectId::RectTwo,\n    Some(vec![MyCustomGroupId::GroupIdOne]),\n    RectToInsert::new(5, 50, 255)\n);\nrects_to_place.push_rect(\n    MyCustomRectId::RectThree,\n    None,\n    RectToInsert::new(30, 30, 255)\n);\n\nlet mut target_bins = BTreeMap::new();\ntarget_bins.insert(MyCustomBinId::DestinationBinOne, TargetBin::new(2048, 2048, 255));\ntarget_bins.insert(MyCustomBinId::DestinationBinTwo, TargetBin::new(4096, 4096, 1020));\n\n// Information about where each `MyCustomRectId` was placed\nlet rectangle_placements = pack_rects(\n    \u0026rects_to_place,\n    \u0026mut target_bins,\n    \u0026volume_heuristic,\n    \u0026contains_smallest_box\n).unwrap();\n```\n\n[Full API Documentation](https://docs.rs/rectangle-pack)\n\n## Background / Initial Motivation\n\nIn my application I've switched to dynamically placing textures into atlases at runtime\ninstead of in how I previously used an asset compilation step, so some of the problems\nexplained in the initial motivation details below are now moot.\n\nI still use rectangle-pack to power my runtime texture allocation, though,\nalong with a handful of other strategies depending on the nature of the\ntextures that need to be placed into the atlas.\n\nrectangle-pack knows nothing about textures, so you can use it for any form of bin\npacking, whether at runtime, during an offline step or any other time you like.\n\n\u003cdetails\u003e\n\u003csummary\u003e\nClick to show the initial motivation for the library.\n\u003c/summary\u003e\n\nI'm working on a game with some of the following texture atlas requirements (as of March 2020):\n\n- I need to be able to guarantee that certain textures are available in the same atlas.\n    - For example - if I'm rendering terrain using a blend map that maps each channel to a color / metallic-roughness / normal texture\n      I want all of those textures to be available in the same atlas.\n      Otherwise in the worst case I might need over a dozen texture uniforms in order to render a single chunk of terrain.\n\n- I want to have control over which channels are used when I'm packing my atlases.\n    - For example - I need to be able to easily pack my metallic and roughness textures into one channel each, while\n      packing color and normal channels into three channels.\n        - This means that my rectangle packer needs to expose configuration on the number of layers/channels available in our target bins.\n\n- I need to be able to ensure that uncommon textures aren't taking up space in commonly used atlases\n    - For example - if a set of textures is only used in one specific region of the game - they shouldn't take up space in an atlas that contains a texture\n      that is used for very common game elements.\n        - This means that the packer needs to cater to some notion of groups or priority so that uncommon textures can be placed separately from common ones.\n    - This allows us to minimize the number of textures in GPU memory at any time since atlases with uncommon texture atlases can be removed after not being in use for some time.\n        - Without meeting this requirement - a large texture might be sitting on the GPU wasting space indefinitely since it shares an atlas with very common textures that will never be evicted.\n    - Note that we might not end up achieving this at the API level. This could potentially be achieved by just having the consumer call the library multiple times using whichever input rectangles they determine to be of\n      similar priority.\n        - Or some other solution.\n\n- I need to be able to pack individual bits within a channel. For example - if I have a texture mask that encodes whether or not a fragment is metallic I want to be able to pack that into a single bit,\n  perhaps within my alpha channel.\n    - This means that our layers concept needs to support multi-dimensional needs. A layer within a layer.\n        - For example - In color space one might be thinking of RGBA channels / layers or be thinking about within the Alpha channel having 255 different sub-layers. Or even a smaller number of variable sized sub-layers.\n          Our API needs to make this simple to represent and pack.\n    - We don't necessarily need to model things that way internally or even expose a multi-layered notion in the API - we just need to enable those use cases - even if we still think of things as one dimension of layers at the API level.\n        - In fact .. as I type this .. one dimensions of layers at the API level both internally and externally sounds much simpler. Let the consumer worry about whether a channel is considered one layer (i.e. alpha) or 255 layers (i.e. every bit in the alpha channel).\n\n- I need to be able to allow one texture to be present in multiple atlases.\n    - For example - say there is a grass texture that is used in every grassy region of the game. Say each of those regions has some textures that are only used in that region and thus relegated to their own\n      atlas. We want to make sure our grass texture is copied into each of those textures so that one texture can support the needs of that region instead of two.\n\nThese requirements are the initial guiding pillars to design the rectangle-pack API.\n\nThe API shouldn't know about the specifics of any of these requirements - it should just provide the bare minimum required to make them possible. We're trying to push as much into user-land as possible and leave\n`rectangle-pack`s responsibility to not much more than answering:\n\n\u003e Given these rectangles that need to be placed, the maximum sizes of the target bins to place them in and some criteria about how to place and how not to place them,\n\u003e where can I put all of these rectangles?\n\n\u003c/details\u003e\n\u003cp\u003e\u003c/p\u003e\n\n## no_std\n\nrectangle-pack supports `no_std` by disabling the `std` feature.\n\n```toml\nrectangle-pack = {version = \"0.4\", default-features = false}\n```\n\nDisabling the `std` feature does the following.\n\n- `BTreeMap`s are used internally in places where `HashMap`s would have been used.\n\n## Features\n\n- Place any number of 2d / 3d rectangles into any number of 2d / 3d target bins.\n  - Supports three dimensional rectangles through a width + height + depth based API.\n\n- Generic API that pushes as much as possible into user-land for maximum flexibility.\n\n- Group rectangles using generic group id's when you need to ensure that certain rectangles will always end up sharing a bin with each other.\n\n- Supports two dimensional rectangles (depth = 1).\n\n- User provided heuristics to grant full control over the packing algorithm.\n\n- Zero dependencies, making it easier to embed it inside of a more use case specific library without introducing bloat.\n\n- Deterministic packing.\n  - Packing of the same inputs using the same heuristics and the same sized target bins will always lead to the same layout.\n    - This is useful anywhere that reproducible builds are useful, such as when generating a texture atlas that is meant to be cached based on the hash of the contents.\n\n- Ability to remove placed rectangles and coalesce neighboring free space.\n\n## Future Work\n\nThe first version of `rectangle-pack` was designed to meet my own needs.\n\nAs such there is functionality that could be useful that was not explored since I did not need it.\n\nHere are some things that could be useful in the future.\n\n### Three-Dimensional Incoming Rectangle Rotation\n\nWhen attempting to place a Rectangle into the smallest available bin section we might want to rotate the rectangle in order to see which orientation produces the best fit.\n\nThis could be accomplished by:\n\n1. The API exposes three booleans for every incoming rectangles, `allow_global_x_axis_rotation`, `allow_global_y_axis_rotation`, `allow_global_z_axis_rotation`.\n\n2. Let's say all three are enabled. When attempting to place the rectangle/box we should attempt it in all 6 possible orientations and then select the best placement (based on the `ComparePotentialContainersFn` heuristic).\n\n3. Return information to the caller about which axis ended up being rotated.\n\n### Mutually exclusive groups\n\nAn example of this is the ability to ensure that certain rectqngle groups are not placed in the same bins.\n\nPerhaps you have two plates (bins) and two groups of cheese (rectangles), one for Alice and one for Bob.\n\nWhen packing you want to ensure that these groups of cheese each end up in a different bin since Alice and Bob don't like to share.\n\n### Stats on how the bins were packed\n\nThings such as the amount of wasted space - or anything else that would allow the caller to compare the results of different combinations of\ntarget bin sizes and heuristics to see which packed the most efficiently.\n\n---\n\nIf you have a use case that isn't supported, go right ahead and open an issue or submit a pull request.\n\n## Packing Algorithm\n\nWe started with the algorithm described in [rectpack2D] and then made some adjustments in order to\nsupport our goal of flexibly supporting all use cases.\n\n\n- The heuristic is provided by the caller instead of having `rectangle-pack` decide on a user provided heuristic.\n\n- When splitting an available section of a bin into two new sections of a bin - we do not decide on how the split should occur arbitrarily.\n  Instead, we base it on the user provided `more_suitable_containers` heuristic function.\n\n- There is a third dimension.\n\n## In The Wild\n\nHere are some known production users of `rectangle-pack`.\n\n- [Akigi](https://akigi.com) uses `rectangle-pack` to power parts of its runtime texture allocation strategy.\n\n- [Bevy](https://github.com/bevyengine/bevy/blob/9ae56e860468aa3158a702cbcf64e511b84a4b1c/crates/bevy_sprite/Cargo.toml#L29) uses `rectangle-pack`\n  to create texture atlases.\n\n## Contributing\n\nIf you have a use case that isn't supported, a question, a patch, or anything else, go right ahead and open an issue or submit a pull request.\n\n## To Test\n\nTo run the test suite.\n\n```sh\n# Clone the repository\ngit clone git@github.com:chinedufn/rectangle-pack.git\ncd rectangle-pack\n\n# Run tests\ncargo test\n```\n\n## See Also\n\n- [rectpack2D]\n    - Inspired parts of our initial implementation\n\n[rectpack2D]: https://github.com/TeamHypersomnia/rectpack2D\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchinedufn%2Frectangle-pack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchinedufn%2Frectangle-pack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchinedufn%2Frectangle-pack/lists"}