///|
pub struct AtlasOptions {
max_width : Int
padding : Int
} derive(Debug, Eq, ToJson, FromJson)
///|
pub struct PackedFrame {
filename : String
source : Rect
packed : Rect
duration : Int
} derive(Debug, Eq, ToJson, FromJson)
///|
pub struct AtlasPlan {
image : String
size : Size
frames : Array[PackedFrame]
occupancy : Double
} derive(Debug, Eq, ToJson, FromJson)
///|
pub suberror AtlasError {
InvalidMaxWidth(Int)
InvalidPadding(Int)
FrameTooWide(String, Int, Int)
} derive(Debug, Eq, ToJson, FromJson)
///|
pub fn AtlasOptions::default() -> AtlasOptions {
{ max_width: 1024, padding: 1 }
}
///|
pub fn pack_rows(
sheet : SpriteSheet,
options? : AtlasOptions = AtlasOptions::default(),
) -> AtlasPlan {
let mut x = options.padding
let mut y = options.padding
let mut row_h = 0
let mut used_area = 0
let packed : Array[PackedFrame] = []
for frame in sheet.frames {
let w = frame.source_size.w
let h = frame.source_size.h
if x + w + options.padding > options.max_width && x > options.padding {
x = options.padding
y += row_h + options.padding
row_h = 0
}
let rect = { x, y, w, h }
packed.push({
filename: frame.filename,
source: frame.frame,
packed: rect,
duration: frame.duration,
})
used_area += rect.area()
x += w + options.padding
if h > row_h {
row_h = h
}
}
let height = y + row_h + options.padding
let area = options.max_width * height
{
image: sheet.image,
size: { w: options.max_width, h: height },
frames: packed,
occupancy: if area == 0 {
0.0
} else {
used_area.to_double() / area.to_double()
},
}
}
///|
pub fn pack_rows_checked(
sheet : SpriteSheet,
options? : AtlasOptions = AtlasOptions::default(),
) -> AtlasPlan raise {
if options.max_width <= 0 {
raise InvalidMaxWidth(options.max_width)
}
if options.padding < 0 {
raise InvalidPadding(options.padding)
}
for frame in sheet.frames {
if frame.source_size.w + options.padding * 2 > options.max_width {
raise FrameTooWide(frame.filename, frame.source_size.w, options.max_width)
}
}
pack_rows(sheet, options~)
}
///|
pub fn AtlasPlan::packed_area(self : AtlasPlan) -> Int {
self.frames.fold(init=0, (total, frame) => total + frame.packed.area())
}
///|
pub fn AtlasPlan::to_json_string(self : AtlasPlan, indent? : Int = 2) -> String {
self.to_json().stringify(indent~)
}