Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: interactivity sync state #203

Merged
merged 11 commits into from
Jul 23, 2024
4 changes: 3 additions & 1 deletion dotlottie-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ name = "dotlottie_player_core"
dotlottie_fms = { path = "../dotlottie-fms" }
thiserror = "1.0.48"
# "emscripten-no-leading-underscore" branch fix this issue -> https://github.com/sebcrozet/instant/issues/35
instant = {git = "https://github.com/hoodmane/instant", branch = "emscripten-no-leading-underscore", features = ["inaccurate"]}
instant = { git = "https://github.com/hoodmane/instant", branch = "emscripten-no-leading-underscore", features = [
"inaccurate",
] }
serde_json = "1.0.107"
serde = { version = "1.0.188", features = ["derive"] }

Expand Down
10 changes: 7 additions & 3 deletions dotlottie-rs/src/dotlottie_player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1145,10 +1145,12 @@ impl DotLottiePlayerContainer {
}

pub fn manifest_string(&self) -> String {
self.runtime.try_read().ok()
self.runtime
.try_read()
.ok()
.and_then(|runtime| runtime.manifest())
.map_or_else(String::new, |manifest| manifest.to_string())
}
}

pub fn is_complete(&self) -> bool {
self.runtime.read().unwrap().is_complete()
Expand Down Expand Up @@ -1765,7 +1767,9 @@ impl DotLottiePlayer {
}

pub fn manifest_string(&self) -> String {
self.player.try_read().map_or_else(|_| String::new(), |player| player.manifest_string())
self.player
.try_read()
.map_or_else(|_| String::new(), |player| player.manifest_string())
}

pub fn is_complete(&self) -> bool {
Expand Down
178 changes: 121 additions & 57 deletions dotlottie-rs/src/state_machine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,7 @@ impl StateMachine {
observers: RwLock::new(Vec::new()),
};

let sm = state_machine.create_state_machine(state_machine_definition, &player);

match sm {
Ok(sm) => Ok(sm),
Err(err) => Err(err),
}
state_machine.create_state_machine(state_machine_definition, &player)
}

pub fn subscribe(&self, observer: Arc<dyn StateMachineObserver>) {
Expand Down Expand Up @@ -113,6 +108,21 @@ impl StateMachine {

pub fn set_numeric_context(&mut self, key: &str, value: f32) {
self.numeric_context.insert(key.to_string(), value);

let s = self.current_state.clone();

// If current state is a sync state, we need to update the frame
if let Some(state) = s {
let unwrapped_state = state.try_read();

if let Ok(state) = unwrapped_state {
let state_value = &*state;

if let State::Sync { .. } = state_value {
self.execute_current_state();
}
}
}
}

pub fn set_string_context(&mut self, key: &str, value: &str) {
Expand Down Expand Up @@ -147,9 +157,22 @@ impl StateMachine {
Ok(parsed_state_machine) => {
// Loop through result json states and create objects for each
for state in parsed_state_machine.states {
match state.r#type {
parser::StateType::PlaybackState => {
let unwrapped_mode = state.mode.unwrap_or("Forward".to_string());
match state {
parser::StateJson::PlaybackState {
name,
animation_id,
r#loop,
autoplay,
mode,
speed,
segment,
background_color,
use_frame_interpolation,
reset_context,
marker,
..
} => {
let unwrapped_mode = mode.unwrap_or("Forward".to_string());
let mode = {
match unwrapped_mode.as_str() {
"Forward" => Mode::Forward,
Expand All @@ -165,44 +188,76 @@ impl StateMachine {
// Fill out a config with the state's values, if absent use default config values
let playback_config = Config {
mode,
loop_animation: state
.r#loop
.unwrap_or(default_config.loop_animation),
speed: state.speed.unwrap_or(default_config.speed),
use_frame_interpolation: state
.use_frame_interpolation
loop_animation: r#loop.unwrap_or(default_config.loop_animation),
speed: speed.unwrap_or(default_config.speed),
use_frame_interpolation: use_frame_interpolation
.unwrap_or(default_config.use_frame_interpolation),
autoplay: state.autoplay.unwrap_or(default_config.autoplay),
segment: state.segment.unwrap_or(default_config.segment),
background_color: state
.background_color
autoplay: autoplay.unwrap_or(default_config.autoplay),
segment: segment.unwrap_or(default_config.segment),
background_color: background_color
.unwrap_or(default_config.background_color),
layout: Layout::default(),
marker: state.marker.unwrap_or(default_config.marker),
marker: marker.unwrap_or(default_config.marker),
};

// Construct a State with the values we've gathered
let new_playback_state = State::Playback {
name: state.name,
name,
config: playback_config,
reset_context: state.reset_context.unwrap_or("".to_string()),
animation_id: state.animation_id.unwrap_or("".to_string()),
reset_context: reset_context.unwrap_or("".to_string()),
animation_id: animation_id.unwrap_or("".to_string()),
transitions: Vec::new(),
};

states.push(Arc::new(RwLock::new(new_playback_state)));
}
parser::StateType::SyncState => {}
parser::StateType::FinalState => {}
parser::StateType::GlobalState => {}
parser::StateJson::SyncState {
name,
animation_id,
background_color,
reset_context,
frame_context_key,
segment,
..
} => {
let mut config = Config::default();

config.background_color =
background_color.unwrap_or(config.background_color);
config.segment = segment.unwrap_or(config.segment);

let new_sync_state = State::Sync {
name,
frame_context_key,
reset_context: reset_context.unwrap_or("".to_string()),
animation_id: animation_id.unwrap_or("".to_string()),
transitions: Vec::new(),
config,
};

states.push(Arc::new(RwLock::new(new_sync_state)));
}
}
}

// Loop through result transitions and create objects for each
for transition in parsed_state_machine.transitions {
match transition.r#type {
parser::TransitionJsonType::Transition => {
let target_state_index = transition.to_state;
match transition {
parser::TransitionJson::Transition {
from_state,
to_state,
guards,
numeric_event,
string_event,
boolean_event,
on_complete_event,
on_pointer_down_event,
on_pointer_up_event,
on_pointer_enter_event,
on_pointer_exit_event,
on_pointer_move_event,
} => {
let target_state_index = to_state;
let mut guards_for_transition: Vec<Guard> = Vec::new();

// Use the provided index to get the state in the vec we've built
Expand All @@ -214,8 +269,8 @@ impl StateMachine {
}

// Loop through transition guards and create equivalent Guard objects
if transition.guards.is_some() {
let guards = transition.guards.unwrap();
if guards.is_some() {
let guards = guards.unwrap();

for guard in guards {
let new_guard = Guard {
Expand All @@ -233,52 +288,52 @@ impl StateMachine {
let mut new_event: Option<Event> = None;

// Capture which event this transition has
if transition.numeric_event.is_some() {
let numeric_event = transition.numeric_event.unwrap();
if numeric_event.is_some() {
let numeric_event = numeric_event.unwrap();
new_event = Some(Event::Numeric {
value: numeric_event.value,
});
state_to_attach_to = transition.from_state as i32;
} else if transition.string_event.is_some() {
let string_event = transition.string_event.unwrap();
state_to_attach_to = from_state as i32;
} else if string_event.is_some() {
let string_event = string_event.unwrap();
new_event = Some(Event::String {
value: string_event.value,
});
state_to_attach_to = transition.from_state as i32;
} else if transition.boolean_event.is_some() {
let boolean_event = transition.boolean_event.unwrap();
state_to_attach_to = from_state as i32;
} else if boolean_event.is_some() {
let boolean_event = boolean_event.unwrap();
new_event = Some(Event::Bool {
value: boolean_event.value,
});
state_to_attach_to = transition.from_state as i32;
} else if transition.on_complete_event.is_some() {
state_to_attach_to = from_state as i32;
} else if on_complete_event.is_some() {
new_event = Some(Event::OnComplete);
state_to_attach_to = transition.from_state as i32;
} else if transition.on_pointer_down_event.is_some() {
state_to_attach_to = from_state as i32;
} else if on_pointer_down_event.is_some() {
// Default to 0.0 0.0 coordinates
// How to manage targets?
// let pointer_down_event = transition.on_pointer_down_event.unwrap();
// let pointer_down_event = on_pointer_down_event.unwrap();
// pointer_down_event.target;
new_event = Some(Event::OnPointerDown { x: 0.0, y: 0.0 });
state_to_attach_to = transition.from_state as i32;
} else if transition.on_pointer_up_event.is_some() {
state_to_attach_to = from_state as i32;
} else if on_pointer_up_event.is_some() {
// Default to 0.0 0.0 coordinates
// How to manage targets?
new_event = Some(Event::OnPointerUp { x: 0.0, y: 0.0 });
state_to_attach_to = transition.from_state as i32;
} else if transition.on_pointer_enter_event.is_some() {
state_to_attach_to = from_state as i32;
} else if on_pointer_enter_event.is_some() {
// Default to 0.0 0.0 coordinates
// How to manage targets?
new_event = Some(Event::OnPointerEnter { x: 0.0, y: 0.0 });
state_to_attach_to = transition.from_state as i32;
} else if transition.on_pointer_exit_event.is_some() {
state_to_attach_to = from_state as i32;
} else if on_pointer_exit_event.is_some() {
new_event = Some(Event::OnPointerExit {});
state_to_attach_to = transition.from_state as i32;
} else if transition.on_pointer_move_event.is_some() {
state_to_attach_to = from_state as i32;
} else if on_pointer_move_event.is_some() {
// Default to 0.0 0.0 coordinates
// How to manage targets?
new_event = Some(Event::OnPointerMove { x: 0.0, y: 0.0 });
state_to_attach_to = transition.from_state as i32;
state_to_attach_to = from_state as i32;
}
if let Some(event) = new_event {
let new_transition = Transition::Transition {
Expand All @@ -289,9 +344,13 @@ impl StateMachine {

// Since the target is valid and transition created, we attach it to the state
if state_to_attach_to < states.len() as i32 {
states[state_to_attach_to as usize]
.write()
.unwrap()
let try_write_state =
states[state_to_attach_to as usize].try_write();

try_write_state
.map_err(|_| StateMachineError::ParsingError {
reason: "Failed to write to state".to_string(),
})?
.add_transition(new_transition);
}
}
Expand Down Expand Up @@ -468,7 +527,12 @@ impl StateMachine {
}

if self.player.is_some() {
unwrapped_state.execute(self.player.as_mut().unwrap());
unwrapped_state.execute(
self.player.as_mut().unwrap(),
&self.string_context,
&self.bool_context,
&self.numeric_context,
);
}
}

Expand Down
Loading
Loading