///|
pub struct TransformOptions {
offset : Point
scale : Int
duration_scale : Int
rename_prefix : String
clamp_to_sheet : Bool
preserve_boxes : Bool
} derive(Debug, Eq, ToJson, FromJson)
///|
pub fn TransformOptions::default() -> TransformOptions {
{
offset: { x: 0, y: 0 },
scale: 1,
duration_scale: 100,
rename_prefix: "",
clamp_to_sheet: false,
preserve_boxes: true,
}
}
///|
pub fn TransformOptions::with_offset(
self : TransformOptions,
offset : Point,
) -> TransformOptions {
{ ..self, offset, }
}
///|
pub fn TransformOptions::with_scale(
self : TransformOptions,
scale : Int,
) -> TransformOptions {
{ ..self, scale: if scale <= 0 { 1 } else { scale } }
}
///|
pub fn TransformOptions::with_duration_scale(
self : TransformOptions,
percent : Int,
) -> TransformOptions {
{ ..self, duration_scale: if percent <= 0 { 1 } else { percent } }
}
///|
pub fn TransformOptions::with_prefix(
self : TransformOptions,
prefix : String,
) -> TransformOptions {
{ ..self, rename_prefix: prefix }
}
///|
fn transform_rect(rect : Rect, options : TransformOptions) -> Rect {
{
x: rect.x * options.scale + options.offset.x,
y: rect.y * options.scale + options.offset.y,
w: rect.w * options.scale,
h: rect.h * options.scale,
}
}
///|
fn transform_point(point : Point, options : TransformOptions) -> Point {
{
x: point.x * options.scale + options.offset.x,
y: point.y * options.scale + options.offset.y,
}
}
///|
fn transform_box(
box : CollisionBox,
options : TransformOptions,
) -> CollisionBox {
{ ..box, rect: transform_rect(box.rect, options) }
}
///|
pub fn transform_frame(
frame : SourceFrame,
options : TransformOptions,
) -> SourceFrame {
let boxes = if options.preserve_boxes {
frame.boxes.map(fn(box) { transform_box(box, options) })
} else {
[]
}
{
..frame,
frame: transform_rect(frame.frame, options),
sprite_source_size: transform_rect(frame.sprite_source_size, options),
source_size: frame.source_size.scale(options.scale),
duration: frame.duration * options.duration_scale / 100,
boxes,
}
}
///|
pub fn transform_frame_name(
frame : SourceFrame,
prefix : String,
) -> SourceFrame {
{ ..frame, filename: prefix + frame.filename }
}
///|
pub fn transform_frame_duration(
frame : SourceFrame,
percent : Int,
) -> SourceFrame {
{ ..frame, duration: frame.duration * percent / 100 }
}
///|
pub fn transform_frame_offset(
frame : SourceFrame,
offset : Point,
) -> SourceFrame {
{ ..frame, frame: frame.frame.translate(offset.x, offset.y) }
}
///|
pub fn transform_frame_scale(frame : SourceFrame, scale : Int) -> SourceFrame {
transform_frame(frame, TransformOptions::default().with_scale(scale))
}
///|
pub fn transform_frames(
frames : Array[SourceFrame],
options : TransformOptions,
) -> Array[SourceFrame] {
frames.map(fn(frame) { transform_frame(frame, options) })
}
///|
pub fn transform_slice_key(
key : SliceKey,
options : TransformOptions,
) -> SliceKey {
{
..key,
bounds: transform_rect(key.bounds, options),
center: key.center.map(fn(rect) { transform_rect(rect, options) }),
pivot: key.pivot.map(fn(point) { transform_point(point, options) }),
}
}
///|
pub fn transform_slice(slice : Slice, options : TransformOptions) -> Slice {
{
..slice,
bounds: transform_rect(slice.bounds, options),
center: slice.center.map(fn(rect) { transform_rect(rect, options) }),
pivot: slice.pivot.map(fn(point) { transform_point(point, options) }),
keys: slice.keys.map(fn(key) { transform_slice_key(key, options) }),
}
}
///|
pub fn transform_slices(
slices : Array[Slice],
options : TransformOptions,
) -> Array[Slice] {
slices.map(fn(slice) { transform_slice(slice, options) })
}
///|
pub fn transform_sheet(
sheet : SpriteSheet,
options : TransformOptions,
) -> SpriteSheet {
{
..sheet,
size: sheet.size.scale(options.scale),
frames: transform_frames(sheet.frames, options),
slices: transform_slices(sheet.slices, options),
}
}
///|
pub fn rename_sheet_frames(sheet : SpriteSheet, prefix : String) -> SpriteSheet {
{
..sheet,
frames: sheet.frames.map(fn(frame) { transform_frame_name(frame, prefix) }),
}
}
///|
pub fn retime_sheet(sheet : SpriteSheet, percent : Int) -> SpriteSheet {
{
..sheet,
frames: sheet.frames.map(fn(frame) {
transform_frame_duration(frame, percent)
}),
}
}
///|
pub fn offset_sheet(sheet : SpriteSheet, offset : Point) -> SpriteSheet {
{
..sheet,
frames: sheet.frames.map(fn(frame) { transform_frame_offset(frame, offset) }),
}
}
///|
pub fn scale_sheet(sheet : SpriteSheet, scale : Int) -> SpriteSheet {
transform_sheet(sheet, TransformOptions::default().with_scale(scale))
}
///|
pub fn strip_collision_boxes(sheet : SpriteSheet) -> SpriteSheet {
{ ..sheet, frames: sheet.frames.map(fn(frame) { { ..frame, boxes: [] } }) }
}
///|
pub fn strip_slices(sheet : SpriteSheet) -> SpriteSheet {
{ ..sheet, slices: [] }
}
///|
pub fn strip_tags(sheet : SpriteSheet) -> SpriteSheet {
{ ..sheet, tags: [] }
}
///|
pub fn select_frames(sheet : SpriteSheet, names : Array[String]) -> SpriteSheet {
{
..sheet,
frames: sheet.frames.filter(fn(frame) { names.contains(frame.filename) }),
}
}
///|
pub fn select_frames_prefix(
sheet : SpriteSheet,
prefix : String,
) -> SpriteSheet {
{ ..sheet, frames: sheet_find_frames(sheet, prefix) }
}
///|
pub fn select_slices_prefix(
sheet : SpriteSheet,
prefix : String,
) -> SpriteSheet {
{
..sheet,
slices: sheet.slices.filter(fn(slice) { slice.name.has_prefix(prefix) }),
}
}
///|
pub fn normalize_frame_positions(sheet : SpriteSheet) -> SpriteSheet {
let min_x = sheet.frames.fold(init=0, (value, frame) => {
if frame.frame.x < value {
frame.frame.x
} else {
value
}
})
let min_y = sheet.frames.fold(init=0, (value, frame) => {
if frame.frame.y < value {
frame.frame.y
} else {
value
}
})
offset_sheet(sheet, { x: -min_x, y: -min_y })
}
///|
pub fn trim_empty_collision_boxes(sheet : SpriteSheet) -> SpriteSheet {
{
..sheet,
frames: sheet.frames.map(fn(frame) {
{ ..frame, boxes: frame.boxes.filter(fn(box) { !box.rect.is_empty() }) }
}),
}
}
///|
pub fn sort_frames_by_filename(sheet : SpriteSheet) -> SpriteSheet {
let frames = sheet.frames.copy()
frames.sort_by(fn(left, right) { left.filename.compare(right.filename) })
{ ..sheet, frames, }
}
///|
pub fn sort_slices_by_name(sheet : SpriteSheet) -> SpriteSheet {
let slices = sheet.slices.copy()
slices.sort_by(fn(left, right) { left.name.compare(right.name) })
{ ..sheet, slices, }
}
///|
pub fn canonicalize_sheet(sheet : SpriteSheet) -> SpriteSheet {
sort_slices_by_name(
sort_frames_by_filename(trim_empty_collision_boxes(sheet)),
)
}
///|
pub fn transform_is_deterministic(
sheet : SpriteSheet,
options : TransformOptions,
) -> Bool {
sheet_to_json(transform_sheet(sheet, options), indent=0) ==
sheet_to_json(transform_sheet(sheet, options), indent=0)
}
///|
pub fn transform_area_delta(before : SpriteSheet, after : SpriteSheet) -> Int {
sheet_metrics(after).total_source_area -
sheet_metrics(before).total_source_area
}
///|
pub fn transform_duration_delta(
before : SpriteSheet,
after : SpriteSheet,
) -> Int {
total_duration(after) - total_duration(before)
}
///|
pub fn transform_frame_count_delta(
before : SpriteSheet,
after : SpriteSheet,
) -> Int {
after.frames.length() - before.frames.length()
}
///|
pub fn transform_preserves_frame_count(
before : SpriteSheet,
after : SpriteSheet,
) -> Bool {
transform_frame_count_delta(before, after) == 0
}
///|
pub fn transform_preserves_tag_count(
before : SpriteSheet,
after : SpriteSheet,
) -> Bool {
before.tags.length() == after.tags.length()
}
///|
pub fn transform_preserves_slice_count(
before : SpriteSheet,
after : SpriteSheet,
) -> Bool {
before.slices.length() == after.slices.length()
}