Skip to content

Commit

Permalink
fix: 🐛 markers emscripten wasm bindings (#96)
Browse files Browse the repository at this point in the history
  • Loading branch information
theashraf authored Mar 11, 2024
1 parent 0214ab1 commit a58d97d
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 57 deletions.
26 changes: 7 additions & 19 deletions dotlottie-ffi/emscripten_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,12 @@ bool load_dotlottie_data(DotLottiePlayer &player, std::string data, uint32_t wid
return player.load_dotlottie_data(data_vector, width, height);
}

val markers(DotLottiePlayer &player)
{
auto markers = player.markers();

val result = val::array();

for (auto &marker : markers)
{
val marker_obj = val::object();
marker_obj.set("name", marker.name);
marker_obj.set("time", marker.time);
marker_obj.set("duration", marker.duration);
result.call<void>("push", marker_obj);
}

return result;
}

EMSCRIPTEN_BINDINGS(DotLottiePlayer)
{

// Register std::vector<float> as VectorFloat for the Config::segments field
register_vector<float>("VectorFloat");
register_vector<Marker>("VectorMarker");
// register_vector<std::string>("VectorString");
// register_vector<ManifestTheme>("VectorManifestTheme");
// register_vector<ManifestAnimation>("VectorManifestAnimation");
Expand All @@ -51,6 +34,11 @@ EMSCRIPTEN_BINDINGS(DotLottiePlayer)
.value("Bounce", Mode::BOUNCE)
.value("ReverseBounce", Mode::REVERSE_BOUNCE);

value_object<Marker>("Marker")
.field("name", &Marker::name)
.field("time", &Marker::time)
.field("duration", &Marker::duration);

value_object<Config>("Config")
.field("autoplay", &Config::autoplay)
.field("loopAnimation", &Config::loop_animation)
Expand Down Expand Up @@ -133,5 +121,5 @@ EMSCRIPTEN_BINDINGS(DotLottiePlayer)
// .function("subscribe", &DotLottiePlayer::subscribe)
// .function("unsubscribe", &DotLottiePlayer::unsubscribe)
.function("isComplete", &DotLottiePlayer::is_complete)
.function("markers", &markers);
.function("markers", &DotLottiePlayer::markers);
}
47 changes: 19 additions & 28 deletions dotlottie-rs/src/dotlottie_player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,40 +111,31 @@ impl DotLottieRuntime {
}

fn start_frame(&self) -> f32 {
let start_frame: f32 = {
if !self.config.marker.is_empty() {
if let Some((time, _)) = self.markers.get(&self.config.marker) {
return *time;
}
}

if self.config.segments.len() == 2 {
return self.config.segments[0];
if !self.config.marker.is_empty() {
if let Some((time, _)) = self.markers.get(&self.config.marker) {
return (*time).max(0.0);
}
}

0.0
};
if self.config.segments.len() == 2 {
return self.config.segments[0].max(0.0);
}

start_frame.clamp(0.0, self.total_frames())
0.0
}

fn end_frame(&self) -> f32 {
let end_frame: f32 =
{
if !self.config.marker.is_empty() {
if let Some((time, duration)) = self.markers.get(&self.config.marker) {
return time + duration;
}
}

if self.config.segments.len() == 2 {
return self.config.segments[1];
}
if !self.config.marker.is_empty() {
if let Some((time, duration)) = self.markers.get(&self.config.marker) {
return (time + duration).min(self.total_frames());
}
}

self.total_frames()
};
if self.config.segments.len() == 2 {
return self.config.segments[1].min(self.total_frames());
}

end_frame.clamp(0.0, self.total_frames())
self.total_frames()
}

pub fn is_loaded(&self) -> bool {
Expand Down Expand Up @@ -595,7 +586,7 @@ impl DotLottieRuntime {
pub fn load_animation_data(&mut self, animation_data: &str, width: u32, height: u32) -> bool {
self.dotlottie_manager = DotLottieManager::new(None).unwrap();

self.markers = extract_markers(animation_data).unwrap_or_default();
self.markers = extract_markers(animation_data);

self.load_animation_common(
|renderer, w, h| renderer.load_data(animation_data, w, h, false),
Expand All @@ -621,7 +612,7 @@ impl DotLottieRuntime {

match first_animation {
Ok(animation_data) => {
self.markers = extract_markers(animation_data.as_str()).unwrap_or_default();
self.markers = extract_markers(animation_data.as_str());

// For the moment we're ignoring manifest values

Expand Down
173 changes: 163 additions & 10 deletions dotlottie-rs/src/markers.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::collections::HashMap;

use serde::{Deserialize, Serialize};
use serde_json::Result;

#[derive(Serialize, Deserialize)]
pub struct Marker {
Expand All @@ -20,20 +19,174 @@ struct Lottie {

pub type MarkersMap = HashMap<String, (f32, f32)>;

pub fn extract_markers(json_data: &str) -> Result<MarkersMap> {
let lottie: Lottie = serde_json::from_str(json_data)?;

pub fn extract_markers(json_data: &str) -> MarkersMap {
let mut markers_map = HashMap::new();

for marker in lottie.markers {
let name = marker.name.trim();
match serde_json::from_str::<Lottie>(json_data) {
Ok(lottie) => {
for marker in lottie.markers {
let name = marker.name.trim();

if name.is_empty() || marker.duration < 0.0 || marker.time < 0.0 {
continue;
}

markers_map.insert(name.to_string(), (marker.time, marker.duration));
}

if name.is_empty() {
continue;
markers_map
}
Err(_) => markers_map,
}
}

markers_map.insert(name.to_string(), (marker.time, marker.duration));
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;

#[test]
fn test_extract_markers_normal() {
let json_data =
json!({
"markers": [
{"cm": "Marker1", "dr": 1.5, "tm": 0.5},
{"cm": "Marker2", "dr": 2.5, "tm": 1.5}
]
})
.to_string();

let markers = extract_markers(&json_data);

assert_eq!(markers.len(), 2);
assert!(markers.contains_key("Marker1"));
assert_eq!(markers["Marker1"], (0.5, 1.5));
assert!(markers.contains_key("Marker2"));
assert_eq!(markers["Marker2"], (1.5, 2.5));
}

Ok(markers_map)
#[test]
fn test_extract_markers_empty_name() {
let json_data =
json!({
"markers": [
{"cm": "", "dr": 1.5, "tm": 0.5},
{"cm": "Marker2", "dr": 2.5, "tm": 1.5}
]
})
.to_string();

let markers = extract_markers(&json_data);

assert_eq!(markers.len(), 1);
assert!(markers.contains_key("Marker2"));
}

#[test]
fn test_extract_markers_invalid_json() {
let json_data = "This is not a valid JSON".to_string();
assert!(extract_markers(&json_data).is_empty());
}

#[test]
fn test_extract_markers_wrong_structure() {
let json_data = json!({"unexpected_field": "unexpected_value"}).to_string();
assert!(extract_markers(&json_data).is_empty());
}

#[test]
fn test_extract_markers_empty() {
let json_data = json!({}).to_string();
assert!(extract_markers(&json_data).is_empty());
}

#[test]
fn test_extract_markers_duplicate_names() {
let json_data =
json!({
"markers": [
{"cm": "Marker1", "dr": 1.5, "tm": 0.5},
{"cm": "Marker1", "dr": 2.5, "tm": 1.5}
]
})
.to_string();

let markers = extract_markers(&json_data);

assert_eq!(markers.len(), 1);
assert!(markers.contains_key("Marker1"));
assert_eq!(markers["Marker1"], (1.5, 2.5));
}

#[test]
fn test_extract_markers_negative_duration() {
let json_data = json!({
"markers": [
{"cm": "Marker1", "dr": -1.5, "tm": 0.5},
{"cm": "Marker2", "dr": 2.5, "tm": 1.5}
]
})
.to_string();

let markers = extract_markers(&json_data);

assert_eq!(markers.len(), 1);
assert!(markers.contains_key("Marker2"));
assert_eq!(markers["Marker2"], (1.5, 2.5));
}

#[test]
fn test_extract_markers_negative_time() {
let json_data = json!({
"markers": [
{"cm": "Marker1", "dr": 1.5, "tm": -0.5},
{"cm": "Marker2", "dr": 2.5, "tm": 1.5}
]
})
.to_string();

let markers = extract_markers(&json_data);

assert_eq!(markers.len(), 1);
assert!(markers.contains_key("Marker2"));
assert_eq!(markers["Marker2"], (1.5, 2.5));
}

#[test]
fn test_extract_markers_large_numbers() {
let json_data = json!({
"markers": [
{"cm": "Marker1", "dr": 1.5, "tm": 1e10},
{"cm": "Marker2", "dr": 2.5, "tm": 1.5}
]
})
.to_string();

let markers = extract_markers(&json_data);

assert_eq!(markers.len(), 2);
assert!(markers.contains_key("Marker1"));
assert_eq!(markers["Marker1"], (1e10, 1.5));
assert!(markers.contains_key("Marker2"));
assert_eq!(markers["Marker2"], (1.5, 2.5));
}

#[test]
fn test_trim_marker_name() {
let json_data = json!({
"markers": [
{"cm": " Marker1 ", "dr": 1.5, "tm": 0.5},
{"cm": "Marker2", "dr": 2.5, "tm": 1.5}
]
})
.to_string();

let markers = extract_markers(&json_data);

assert_eq!(markers.len(), 2);
assert!(markers.contains_key("Marker1"));
assert_eq!(markers["Marker1"], (0.5, 1.5));
assert!(markers.contains_key("Marker2"));
assert_eq!(markers["Marker2"], (1.5, 2.5));
}
}

0 comments on commit a58d97d

Please sign in to comment.