///|
pub(all) struct FrameNameParts {
original : String
stem : String
suffix_number : Int
has_number : Bool
} derive(Eq, @debug.Debug)
///|
pub fn FrameNameParts::new(
original : String,
stem : String,
suffix_number? : Int = -1,
has_number? : Bool = false,
) -> FrameNameParts {
{ original, stem, suffix_number, has_number }
}
///|
fn is_ascii_digit_code(code : Int) -> Bool {
code >= 48 && code <= 57
}
///|
fn parse_decimal_suffix(text : String) -> Int? {
if text.length() == 0 {
None
} else {
let mut value = 0
for i in 0.. FrameNameParts {
match name.rev_find("_") {
None => FrameNameParts::new(name, name)
Some(pos) => {
let stem = name[:pos].to_owned()
let suffix = name[pos + 1:].to_owned()
match parse_decimal_suffix(suffix) {
Some(value) =>
FrameNameParts::new(name, stem, suffix_number=value, has_number=true)
None => FrameNameParts::new(name, name)
}
}
}
}
///|
pub(all) struct FrameNameGroup {
stem : String
count : Int
first_frame_index : Int
last_frame_index : Int
numeric_count : Int
min_number : Int
max_number : Int
} derive(Eq, @debug.Debug)
///|
pub fn FrameNameGroup::new(stem : String, frame : FrameInfo) -> FrameNameGroup {
let parts = split_frame_name(frame.name)
{
stem,
count: 1,
first_frame_index: frame.index,
last_frame_index: frame.index,
numeric_count: if parts.has_number {
1
} else {
0
},
min_number: if parts.has_number {
parts.suffix_number
} else {
-1
},
max_number: if parts.has_number {
parts.suffix_number
} else {
-1
},
}
}
///|
pub fn FrameNameGroup::is_numbered(self : FrameNameGroup) -> Bool {
self.numeric_count == self.count && self.count > 0
}
///|
pub fn FrameNameGroup::has_number_gap(self : FrameNameGroup) -> Bool {
self.is_numbered() &&
self.min_number >= 0 &&
self.max_number - self.min_number + 1 != self.count
}
///|
fn update_group(group : FrameNameGroup, frame : FrameInfo) -> FrameNameGroup {
let parts = split_frame_name(frame.name)
let numeric_inc = if parts.has_number { 1 } else { 0 }
{
..group,
count: group.count + 1,
last_frame_index: frame.index,
numeric_count: group.numeric_count + numeric_inc,
min_number: if parts.has_number &&
(group.min_number < 0 || parts.suffix_number < group.min_number) {
parts.suffix_number
} else {
group.min_number
},
max_number: if parts.has_number && parts.suffix_number > group.max_number {
parts.suffix_number
} else {
group.max_number
},
}
}
///|
fn find_group_index(groups : Array[FrameNameGroup], stem : String) -> Int {
for i, group in groups {
if group.stem == stem {
return i
}
}
-1
}
///|
pub fn group_frame_names(sheet : SpriteSheet) -> Array[FrameNameGroup] {
let groups : Array[FrameNameGroup] = []
for frame in sheet.frames {
let parts = split_frame_name(frame.name)
let index = find_group_index(groups, parts.stem)
if index < 0 {
groups.push(FrameNameGroup::new(parts.stem, frame))
} else {
groups[index] = update_group(groups[index], frame)
}
}
groups
}
///|
pub(all) struct NamingIssue {
stem : String
code : String
message : String
} derive(Eq, @debug.Debug)
///|
pub fn NamingIssue::new(
stem : String,
code : String,
message : String,
) -> NamingIssue {
{ stem, code, message }
}
///|
pub fn inspect_frame_name_groups(sheet : SpriteSheet) -> Array[NamingIssue] {
let issues : Array[NamingIssue] = []
for group in group_frame_names(sheet) {
if group.has_number_gap() {
issues.push(
NamingIssue::new(
group.stem,
"number-gap",
"numbered frame sequence has a gap in suffix values",
),
)
}
if group.count == 1 && group.stem.find("_") != None {
issues.push(
NamingIssue::new(
group.stem,
"single-frame-group",
"frame name looks grouped but only one frame was found",
),
)
}
}
issues
}
///|
pub fn naming_report_text(sheet : SpriteSheet) -> String {
let groups = group_frame_names(sheet)
let issues = inspect_frame_name_groups(sheet)
let lines : Array[String] = []
lines.push("name-groups=\{groups.length()}")
for group in groups {
lines.push(
"group " +
group.stem +
" count=\{group.count} frames=\{group.first_frame_index}..\{group.last_frame_index}",
)
}
if issues.length() > 0 {
lines.push("naming-issues=\{issues.length()}")
for issue in issues {
lines.push("- " + issue.code + " " + issue.stem + ": " + issue.message)
}
} else {
lines.push("naming-issues=0")
}
join_strings(lines, "\n")
}