///|
pub struct FrameMetrics {
filename : String
frame_area : Int
source_area : Int
duration : Int
trimmed : Bool
rotated : Bool
collision_boxes : Int
} derive(Debug, Eq, ToJson, FromJson)
///|
pub struct SheetMetrics {
image : String
frame_count : Int
tag_count : Int
slice_count : Int
total_frame_area : Int
total_source_area : Int
total_duration : Int
trimmed_frames : Int
rotated_frames : Int
collision_boxes : Int
} derive(Debug, Eq, ToJson, FromJson)
///|
pub struct AtlasMetrics {
width : Int
height : Int
frame_count : Int
packed_area : Int
occupancy : Double
padding_gaps : Int
} derive(Debug, Eq, ToJson, FromJson)
///|
pub struct AnimationMetrics {
name : String
frame_count : Int
total_duration : Int
unique_frame_count : Int
looped : Bool
} derive(Debug, Eq, ToJson, FromJson)
///|
pub fn frame_metrics(frame : SourceFrame) -> FrameMetrics {
{
filename: frame.filename,
frame_area: frame.frame.area(),
source_area: frame.source_size.area(),
duration: frame.duration,
trimmed: frame.trimmed,
rotated: frame.rotated,
collision_boxes: frame.boxes.length(),
}
}
///|
pub fn sheet_metrics(sheet : SpriteSheet) -> SheetMetrics {
let mut frame_area = 0
let mut source_area = 0
let mut duration = 0
let mut trimmed = 0
let mut rotated = 0
let mut boxes = 0
for frame in sheet.frames {
frame_area += frame.frame.area()
source_area += frame.source_size.area()
duration += frame.duration
if frame.trimmed {
trimmed += 1
}
if frame.rotated {
rotated += 1
}
boxes += frame.boxes.length()
}
{
image: sheet.image,
frame_count: sheet.frames.length(),
tag_count: sheet.tags.length(),
slice_count: sheet.slices.length(),
total_frame_area: frame_area,
total_source_area: source_area,
total_duration: duration,
trimmed_frames: trimmed,
rotated_frames: rotated,
collision_boxes: boxes,
}
}
///|
pub fn atlas_metrics(atlas : AtlasPlan) -> AtlasMetrics {
{
width: atlas.size.w,
height: atlas.size.h,
frame_count: atlas.frames.length(),
packed_area: atlas.packed_area(),
occupancy: atlas.occupancy,
padding_gaps: atlas.padding_gaps(),
}
}
///|
pub fn animation_metrics(clip : AnimationClip) -> AnimationMetrics {
let names : Array[String] = []
for name in clip.frames {
if !names.contains(name) {
names.push(name)
}
}
{
name: clip.name,
frame_count: clip.frames.length(),
total_duration: clip.total_duration(),
unique_frame_count: names.length(),
looped: clip.loop_mode == Loop,
}
}
///|
pub fn all_frame_metrics(sheet : SpriteSheet) -> Array[FrameMetrics] {
sheet.frames.map(frame_metrics)
}
///|
pub fn all_animation_metrics(
machine : AnimationMachine,
) -> Array[AnimationMetrics] {
machine.clips.map(animation_metrics)
}
///|
pub fn average_frame_duration(sheet : SpriteSheet) -> Double {
if sheet.frames.length() == 0 {
0.0
} else {
total_duration(sheet).to_double() / sheet.frames.length().to_double()
}
}
///|
pub fn frame_area_ratio(frame : SourceFrame) -> Double {
let source = frame.source_size.area()
if source <= 0 {
0.0
} else {
frame.frame.area().to_double() / source.to_double()
}
}
///|
pub fn sheet_trim_ratio(sheet : SpriteSheet) -> Double {
if sheet.frames.length() == 0 {
0.0
} else {
sheet_metrics(sheet).trimmed_frames.to_double() /
sheet.frames.length().to_double()
}
}
///|
pub fn sheet_source_occupancy(sheet : SpriteSheet) -> Double {
let area = sheet.size.area()
if area <= 0 {
0.0
} else {
sheet_metrics(sheet).total_frame_area.to_double() / area.to_double()
}
}
///|
pub fn machine_duration(machine : AnimationMachine) -> Int {
machine.clips.fold(init=0, (total, clip) => total + clip.total_duration())
}
///|
pub fn machine_unique_frame_names(machine : AnimationMachine) -> Array[String] {
let names : Array[String] = []
for clip in machine.clips {
for frame in clip.frames {
if !names.contains(frame) {
names.push(frame)
}
}
}
names
}
///|
pub fn sheet_frame_names(sheet : SpriteSheet) -> Array[String] {
sheet.frames.map(fn(frame) { frame.filename })
}
///|
pub fn sheet_tag_names(sheet : SpriteSheet) -> Array[String] {
sheet.tags.map(fn(tag) { tag.name })
}
///|
pub fn sheet_slice_names(sheet : SpriteSheet) -> Array[String] {
sheet.slices.map(fn(slice) { slice.name })
}
///|
pub fn sheet_find_frames(
sheet : SpriteSheet,
prefix : String,
) -> Array[SourceFrame] {
sheet.frames.filter(fn(frame) { frame.filename.has_prefix(prefix) })
}
///|
pub fn sheet_frames_with_boxes(sheet : SpriteSheet) -> Array[SourceFrame] {
sheet.frames.filter(fn(frame) { frame.boxes.length() > 0 })
}
///|
pub fn sheet_frames_with_duration(
sheet : SpriteSheet,
minimum : Int,
) -> Array[SourceFrame] {
sheet.frames.filter(fn(frame) { frame.duration >= minimum })
}
///|
pub fn sheet_largest_frame(sheet : SpriteSheet) -> SourceFrame? {
match sheet.frames.get(0) {
None => None
Some(first) => {
let mut result = first
for frame in sheet.frames[1:] {
if frame.source_size.area() > result.source_size.area() {
result = frame
}
}
Some(result)
}
}
}
///|
pub fn sheet_longest_frame(sheet : SpriteSheet) -> SourceFrame? {
match sheet.frames.get(0) {
None => None
Some(first) => {
let mut result = first
for frame in sheet.frames[1:] {
if frame.duration > result.duration {
result = frame
}
}
Some(result)
}
}
}
///|
pub fn sheet_frame_index(sheet : SpriteSheet, filename : String) -> Int? {
sheet.frames.search_by(fn(frame) { frame.filename == filename })
}
///|
pub fn sheet_tag_index(sheet : SpriteSheet, name : String) -> Int? {
sheet.tags.search_by(fn(tag) { tag.name == name })
}
///|
pub fn sheet_slice_index(sheet : SpriteSheet, name : String) -> Int? {
sheet.slices.search_by(fn(slice) { slice.name == name })
}
///|
pub fn atlas_frame_names(atlas : AtlasPlan) -> Array[String] {
atlas.frames.map(fn(frame) { frame.filename })
}
///|
pub fn atlas_source_bounds(atlas : AtlasPlan) -> Rect? {
rect_bounds(atlas.frames.map(fn(frame) { frame.packed }))
}
///|
pub fn atlas_has_overlap(atlas : AtlasPlan) -> Bool {
rects_overlap_any(atlas.frames.map(fn(frame) { frame.packed }))
}
///|
pub fn atlas_is_deterministic(left : AtlasPlan, right : AtlasPlan) -> Bool {
left.to_json_string(indent=0) == right.to_json_string(indent=0)
}
///|
pub fn animation_contains(clip : AnimationClip, filename : String) -> Bool {
clip.frames.contains(filename)
}
///|
pub fn machine_contains_clip(machine : AnimationMachine, name : String) -> Bool {
machine.clip_named(name) is Some(_)
}
///|
pub fn machine_transition_count(machine : AnimationMachine) -> Int {
machine.transitions.length()
}
///|
pub fn diagnostics_error_count(sheet : SpriteSheet) -> Int {
summarize_diagnostics(validate_sheet(sheet)).errors
}
///|
pub fn diagnostics_warning_count(sheet : SpriteSheet) -> Int {
summarize_diagnostics(validate_sheet(sheet)).warnings
}
///|
pub fn sheet_is_valid(sheet : SpriteSheet) -> Bool {
diagnostics_error_count(sheet) == 0
}
///|
pub fn sheet_summary(sheet : SpriteSheet) -> String {
let metrics = sheet_metrics(sheet)
"\{metrics.image}: \{metrics.frame_count} frames, \{metrics.tag_count} tags, \{metrics.slice_count} slices"
}