///|
/// Sort strategy used before packing starts.
pub(all) enum SortOrder {
InputOrder
AreaDesc
HeightDesc
WidthDesc
PerimeterDesc
} derive(Eq, @debug.Debug)
///|
/// Packing algorithm.
pub(all) enum Algorithm {
ShelfFirstFit
GuillotineBestArea
SkylineBottomLeft
} derive(Eq, @debug.Debug)
///|
/// Rectangle item requested by a caller.
pub(all) struct Rect {
id : String
width : Int
height : Int
} derive(Eq, @debug.Debug)
///|
/// Two-dimensional bin where rectangles are placed.
pub(all) struct Bin {
width : Int
height : Int
} derive(Eq, @debug.Debug)
///|
/// Outer padding reserves a border around the bin. Inner padding is inserted
/// between placements by the built-in algorithms.
pub(all) struct Padding {
outer : Int
inner : Int
} derive(Eq, @debug.Debug)
///|
/// Complete pack configuration.
pub(all) struct PackOptions {
bin : Bin
padding : Padding
sort : SortOrder
algorithm : Algorithm
allow_rotate : Bool
} derive(Eq, @debug.Debug)
///|
/// One successful placement.
pub(all) struct Placed {
id : String
x : Int
y : Int
width : Int
height : Int
rotated : Bool
original_width : Int
original_height : Int
} derive(Eq, @debug.Debug)
///|
/// Resulting layout and the items that did not fit.
pub(all) struct Layout {
bin : Bin
placements : Array[Placed]
unpacked : Array[Rect]
algorithm : Algorithm
occupancy_permille : Int
waste_area : Int
} derive(Eq, @debug.Debug)
///|
/// One packed page in a multipage result.
pub(all) struct Page {
index : Int
layout : Layout
} derive(Eq, @debug.Debug)
///|
/// Multipage result for inputs that do not fit in a single bin.
pub(all) struct MultiLayout {
pages : Array[Page]
unpacked : Array[Rect]
total_occupancy_permille : Int
} derive(Eq, @debug.Debug)
///|
/// Parsed text specification.
pub(all) struct Spec {
options : PackOptions
rects : Array[Rect]
} derive(Eq, @debug.Debug)
///|
/// Aggregate information about a source rectangle collection.
pub(all) struct RectSetStats {
count : Int
total_area : Int
min_width : Int
max_width : Int
min_height : Int
max_height : Int
min_area : Int
max_area : Int
average_width_permille : Int
average_height_permille : Int
average_area_permille : Int
square_count : Int
wide_count : Int
tall_count : Int
oversized_count : Int
} derive(Eq, @debug.Debug)
///|
/// Derived metrics for a completed layout.
pub(all) struct LayoutStats {
placed_count : Int
unpacked_count : Int
rotated_count : Int
used_area : Int
container_area : Int
padded_capacity : Int
occupancy_permille : Int
padded_occupancy_permille : Int
waste_area : Int
bounding_x : Int
bounding_y : Int
bounding_width : Int
bounding_height : Int
bounding_area : Int
bounding_occupancy_permille : Int
row_count : Int
column_count : Int
horizontal_span : Int
vertical_span : Int
fragmentation_score : Int
compactness_score : Int
} derive(Eq, @debug.Debug)
///|
/// One candidate produced by automatic option exploration.
pub(all) struct PackCandidate {
options : PackOptions
layout : Layout
score : Int
rank : Int
} derive(Eq, @debug.Debug)
///|
/// Result of comparing several option combinations for the same inputs.
pub(all) struct PackComparison {
base_options : PackOptions
candidates : Array[PackCandidate]
best : PackCandidate
} derive(Eq, @debug.Debug)
///|
/// A coarse grid cell used by density and heatmap reports.
pub(all) struct DensityCell {
col : Int
row : Int
x : Int
y : Int
width : Int
height : Int
used_area : Int
capacity : Int
occupancy_permille : Int
} derive(Eq, @debug.Debug)
///|
/// Coarse grid summary of a layout.
pub(all) struct GridReport {
cell_width : Int
cell_height : Int
cols : Int
rows : Int
cells : Array[DensityCell]
occupied_cells : Int
empty_cells : Int
densest_cell : DensityCell
sparsest_cell : DensityCell
} derive(Eq, @debug.Debug)
///|
/// Severity used by diagnostic reports.
pub(all) enum IssueSeverity {
Info
Warning
Critical
} derive(Eq, @debug.Debug)
///|
/// Machine-readable category for layout diagnostics.
pub(all) enum IssueKind {
LowOccupancy
HasUnpacked
WideBoundingBox
TallBoundingBox
SparseGrid
HeavyRotation
FragmentedRows
ConstraintFailed
SourceRisk
} derive(Eq, @debug.Debug)
///|
/// One diagnostic finding.
pub(all) struct LayoutIssue {
severity : IssueSeverity
kind : IssueKind
id : String
message : String
value : Int
} derive(Eq, @debug.Debug)
///|
/// Optional policy checks for callers that need production constraints.
pub(all) struct LayoutConstraints {
min_occupancy_permille : Int
max_waste_permille : Int
min_gap : Int
max_row_count : Int
max_column_count : Int
max_rotated_permille : Int
require_all_packed : Bool
} derive(Eq, @debug.Debug)
///|
/// One failed production constraint.
pub(all) struct ConstraintViolation {
name : String
expected : Int
actual : Int
severity : IssueSeverity
} derive(Eq, @debug.Debug)
///|
/// Built-in fixture families for examples, smoke tests, and benchmarks.
pub(all) enum FixtureKind {
SpriteSheet
UiPanels
DocumentBlocks
MixedIcons
StressSlivers
} derive(Eq, @debug.Debug)
///|
/// Change type for comparing two layouts.
pub(all) enum PlacementChangeKind {
Added
Removed
Moved
Resized
RotationChanged
Unchanged
} derive(Eq, @debug.Debug)
///|
/// One placement-level difference between two layouts.
pub(all) struct PlacementChange {
id : String
kind : PlacementChangeKind
old_x : Int
old_y : Int
old_width : Int
old_height : Int
new_x : Int
new_y : Int
new_width : Int
new_height : Int
area_delta : Int
} derive(Eq, @debug.Debug)
///|
/// Summary of differences between two layouts.
pub(all) struct LayoutDiff {
left_algorithm : Algorithm
right_algorithm : Algorithm
left_occupancy_permille : Int
right_occupancy_permille : Int
occupancy_delta : Int
added_count : Int
removed_count : Int
moved_count : Int
resized_count : Int
rotation_changed_count : Int
unchanged_count : Int
changes : Array[PlacementChange]
} derive(Eq, @debug.Debug)
///|
/// Errors are explicit so callers can surface actionable diagnostics.
pub(all) enum PackError {
InvalidBin(width~ : Int, height~ : Int)
InvalidPadding(outer~ : Int, inner~ : Int)
InvalidRect(id~ : String, width~ : Int, height~ : Int)
InvalidPageLimit(limit~ : Int)
InvalidCellSize(width~ : Int, height~ : Int)
DuplicateId(id~ : String)
OutOfBounds(id~ : String)
Overlap(left~ : String, right~ : String)
ParseError(line~ : Int, message~ : String)
} derive(Eq, @debug.Debug)
///|
/// Convenience rectangle constructor.
pub fn Rect::make(id : StringView, width : Int, height : Int) -> Rect {
Rect::{ id: id.to_owned(), width, height }
}
///|
/// Convenience bin constructor.
pub fn Bin::make(width : Int, height : Int) -> Bin {
Bin::{ width, height }
}
///|
/// Convenience padding constructor.
pub fn Padding::make(outer : Int, inner : Int) -> Padding {
Padding::{ outer, inner }
}
///|
/// Recommended default options for deterministic asset-style packing.
pub fn PackOptions::default(width : Int, height : Int) -> PackOptions {
PackOptions::{
bin: Bin::{ width, height },
padding: Padding::{ outer: 0, inner: 0 },
sort: AreaDesc,
algorithm: ShelfFirstFit,
allow_rotate: false,
}
}
///|
/// Pack rectangles using the algorithm selected by `options`.
pub fn pack(
rects : ArrayView[Rect],
options : PackOptions,
) -> Result[Layout, PackError] {
match validate_input(rects, options) {
Err(err) => Err(err)
Ok(_) =>
match options.algorithm {
ShelfFirstFit => Ok(pack_shelf(rects, options))
GuillotineBestArea => Ok(pack_guillotine(rects, options))
SkylineBottomLeft => Ok(pack_skyline(rects, options))
}
}
}
///|
/// Pack a textual spec in one call.
pub fn pack_spec(source : String) -> Result[Layout, PackError] {
match parse_spec(source) {
Err(err) => Err(err)
Ok(spec) => pack(spec.rects, spec.options)
}
}
///|
/// Built-in sample used by the CLI smoke test and README.
pub fn sample_spec() -> String {
"bin 64 32\npadding 1 1\nalgorithm guillotine\nsort area\nrotate true\nrect hero 18 12\nrect logo 13 7\nrect icon 8 8\nrect badge 9 5\nrect chip 5 4\n"
}
///|
/// Built-in sample layout. The return type stays explicit so CI catches
/// regressions in the example path.
pub fn sample_layout() -> Result[Layout, PackError] {
pack_spec(sample_spec())
}
///|
/// Stable display text for errors.
pub fn error_message(err : PackError) -> String {
match err {
InvalidBin(width~, height~) => "invalid bin size \{width}x\{height}"
InvalidPadding(outer~, inner~) =>
"invalid padding outer=\{outer} inner=\{inner}"
InvalidRect(id~, width~, height~) =>
"invalid rectangle '\{id}' size \{width}x\{height}"
InvalidPageLimit(limit~) => "invalid page limit \{limit}"
InvalidCellSize(width~, height~) =>
"invalid preview cell size \{width}x\{height}"
DuplicateId(id~) => "duplicate id '\{id}'"
OutOfBounds(id~) => "placement '\{id}' is outside the bin"
Overlap(left~, right~) => "placements '\{left}' and '\{right}' overlap"
ParseError(line~, message~) => "line \{line}: \{message}"
}
}