///|
fn page_occupancy_permille(pages : ArrayView[Page]) -> Int {
if pages.is_empty() {
return 0
}
let mut used = 0
let mut capacity = 0
for page in pages {
used = used + total_placed_area(page.layout.placements)
capacity = capacity + bin_area(page.layout.bin)
}
if capacity <= 0 {
0
} else {
used * 1000 / capacity
}
}
///|
fn strip_unpacked(layout : Layout) -> Layout {
make_layout(layout.bin, layout.placements, [], layout.algorithm)
}
///|
/// Pack rectangles across multiple pages with the same bin/options.
pub fn pack_pages(
rects : ArrayView[Rect],
options : PackOptions,
max_pages : Int,
) -> Result[MultiLayout, PackError] {
if max_pages <= 0 {
return Err(InvalidPageLimit(limit=max_pages))
}
let pages : Array[Page] = []
let mut remaining = rects.to_owned()
let mut page_index = 0
while page_index < max_pages && !remaining.is_empty() {
let layout = match pack(remaining, options) {
Ok(value) => value
Err(err) => return Err(err)
}
if layout.placements.is_empty() {
break
}
pages.push(Page::{ index: page_index, layout: strip_unpacked(layout) })
remaining = layout.unpacked
page_index = page_index + 1
}
Ok(MultiLayout::{
pages,
unpacked: remaining,
total_occupancy_permille: page_occupancy_permille(pages),
})
}
///|
/// Parse a spec and pack it across several pages.
pub fn pack_pages_spec(
source : String,
max_pages : Int,
) -> Result[MultiLayout, PackError] {
match parse_spec(source) {
Err(err) => Err(err)
Ok(spec) => pack_pages(spec.rects, spec.options, max_pages)
}
}
///|
/// Human-readable multipage report.
pub fn to_pages_report(result : MultiLayout) -> String {
let out = StringBuilder()
out.write_string(
"rectloom pages=\{result.pages.length()} unpacked=\{result.unpacked.length()} total_occupancy=\{result.total_occupancy_permille}/1000\n",
)
for page in result.pages {
out.write_string("\npage \{page.index}\n")
out.write_string(to_report(page.layout))
}
if !result.unpacked.is_empty() {
out.write_string("\nremaining unpacked:\n")
for rect in result.unpacked {
out.write_string("- \{rect.id}: \{rect.width}x\{rect.height}\n")
}
}
out.to_string()
}
///|
/// Stable JSON manifest for multipage packing.
pub fn to_pages_json_manifest(result : MultiLayout) -> String {
let out = StringBuilder()
out.write_string("{\n")
out.write_string(
" \"total_occupancy_permille\": \{result.total_occupancy_permille},\n",
)
out.write_string(" \"pages\": [\n")
for i in 0..