Easing function tool
parent
1ae417af5c
commit
9e1014bbd9
|
@ -19,4 +19,6 @@ linked-hash-map = "0.5.6"
|
|||
convert_case = "0.6.0"
|
||||
roxmltree = "0.18.0"
|
||||
regex = "1.9.5"
|
||||
euclid = "0.22.9"
|
||||
euclid = "0.22.9"
|
||||
flo_curves = "0.7.2"
|
||||
fixed = "1.24.0"
|
|
@ -1,6 +1,6 @@
|
|||
use std::{error::Error, fs::File, io::Write, path::Path, collections::HashMap};
|
||||
use clap::Parser;
|
||||
use crate::reskit::{tileset, soundtrack::{formats::dmf::DmfModule, engines::echo::engine::{EchoFormat, EchoArtifact}}, utility::print_good, level::{converter::get_tiled_tilemap, system::{get_tiles, get_code, get_tilemap, get_collision_map, get_objs, get_sprites}}};
|
||||
use crate::reskit::{tileset, soundtrack::{formats::dmf::DmfModule, engines::echo::engine::{EchoFormat, EchoArtifact}}, utility::print_good, level::{converter::get_tiled_tilemap, system::{get_tiles, get_code, get_tilemap, get_collision_map, get_objs, get_sprites}}, easing::get_cubic_bezier};
|
||||
use super::settings::{Args, Tools, TileOutputFormat, TileOrder};
|
||||
|
||||
pub fn run_command() -> Result<(), Box<dyn Error>> {
|
||||
|
@ -129,6 +129,25 @@ pub fn run_command() -> Result<(), Box<dyn Error>> {
|
|||
code_asm.write_all( &get_code( &tiled_file, "testlevel", "levels/" )?.as_bytes() )?;
|
||||
print_good( "exported level.asm" );
|
||||
}
|
||||
Tools::Easing { output_directory, interval, point_1, point_2 } => {
|
||||
let cp1: Vec<&str> = point_1.split( "," ).collect();
|
||||
if cp1.len() != 2 {
|
||||
return Err( format!( "invalid format for point_1: {}", point_1 ) )?;
|
||||
}
|
||||
|
||||
let ( cp1_x, cp1_y ): ( f64, f64 ) = ( cp1[ 0 ].parse()?, cp1[ 1 ].parse()? );
|
||||
|
||||
let cp2: Vec<&str> = point_1.split( "," ).collect();
|
||||
if cp2.len() != 2 {
|
||||
return Err( format!( "invalid format for point_2: {}", point_2 ) )?;
|
||||
}
|
||||
|
||||
let ( cp2_x, cp2_y ): ( f64, f64 ) = ( cp2[ 0 ].parse()?, cp2[ 1 ].parse()? );
|
||||
|
||||
let mut easing = File::create( format!( "{}curve.bin", output_directory ) )?;
|
||||
easing.write_all( &get_cubic_bezier( ( 0.0, 0.0 ), ( cp1_x, cp1_y ), ( cp2_x, cp2_y ), ( 1.0, 1.0 ), interval )? )?;
|
||||
print_good( "exported curve.bin" );
|
||||
}
|
||||
};
|
||||
|
||||
Ok( () )
|
||||
|
|
|
@ -124,5 +124,27 @@ pub enum Tools {
|
|||
/// Console system type
|
||||
#[arg(short, long, value_enum, default_value_t=SystemType::Md)]
|
||||
console: SystemType
|
||||
},
|
||||
|
||||
#[command(name = "easing")]
|
||||
#[command(about = "Generate an easing curve given a time interval and control points.")]
|
||||
Easing {
|
||||
|
||||
/// Output directory for artifacts
|
||||
#[arg(short, long, default_value_t=String::from("./"))]
|
||||
output_directory: String,
|
||||
|
||||
/// Time span of the easing curve (in 1/60 s increments)
|
||||
#[arg(short,long)]
|
||||
interval: u16,
|
||||
|
||||
/// First control point of the cubic bezier (format: <x>,<y>)
|
||||
#[arg(long)]
|
||||
point_1: String,
|
||||
|
||||
/// Second control point of the cubic bezier (format: <x>,<y>)
|
||||
#[arg(long)]
|
||||
point_2: String
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
use std::error::Error;
|
||||
use fixed::FixedI32;
|
||||
use fixed::types::extra::U16;
|
||||
use flo_curves::*;
|
||||
use flo_curves::{bezier, Coord2};
|
||||
|
||||
use super::utility::print_info;
|
||||
|
||||
/*
|
||||
stinkhead7ds easing curve description:
|
||||
|
||||
e.g. going from 120 to 140
|
||||
easing function is cubic bezier going from 0.0 to 1.0
|
||||
|
||||
140-120=20 increments
|
||||
0.0 is +0 and 1.0 is +20
|
||||
so e.g. if you read a 0.5 at the given increment it's 0.5 * 20 = +10
|
||||
|
||||
curves are exported from reskit sampled at 1/60 increments
|
||||
reskit accepts arguments for control points and exports 16.16 fixed point binary files
|
||||
so a 1 second animation is 60 increments, 2 seconds is 120, etc.
|
||||
|
||||
each iteration be like:
|
||||
1. take cubic bezier sample and post increment
|
||||
2. the value at this iteration is (final - beginning) * sample
|
||||
a. so example (140-120)*0.5 for a linear curve sampled right in the middle
|
||||
3. round the value to the nearest whole integer
|
||||
4. do not go back to 1 if we exhausted the curve
|
||||
|
||||
need fixed point arithmetic multiplication
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Output a binary for a cubic bezier defined by the given four points and the
|
||||
* time interval defined as number of 1/60 jiffies.
|
||||
*/
|
||||
pub fn get_cubic_bezier( p0: (f64, f64), c0: (f64, f64), c1: (f64, f64), p1: (f64, f64), jiffies: u16 ) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
let mut result: Vec<u8> = Vec::new();
|
||||
|
||||
// 2 bytes: Number of frames this curve spans
|
||||
result.extend( jiffies.to_be_bytes() );
|
||||
|
||||
let curve = bezier::Curve::from_points(
|
||||
Coord2( p0.0, p0.1 ),
|
||||
( Coord2( c0.0, c0.1 ), Coord2( c1.0, c1.1 ) ),
|
||||
Coord2( p1.0, p1.1 )
|
||||
);
|
||||
|
||||
let mut current_jiffy = 0.0;
|
||||
// (jiffies) of 16.16 fixed point integers: Each point in the curve at the given frame
|
||||
for i in 0..jiffies {
|
||||
let point = curve.point_at_pos( current_jiffy );
|
||||
let x = point.1;
|
||||
print_info( &format!( "at iteration {}, curve is {:?}, 16.16 format will be {}", i, point, x ) );
|
||||
current_jiffy += 1.0 / jiffies as f64;
|
||||
|
||||
let as_fixed = FixedI32::<U16>::from_num( x );
|
||||
result.extend( as_fixed.to_be_bytes() );
|
||||
}
|
||||
|
||||
Ok( result )
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
pub mod cli;
|
||||
pub mod easing;
|
||||
pub mod level;
|
||||
pub mod soundtrack;
|
||||
pub mod tileset;
|
||||
|
|
Loading…
Reference in New Issue