#![cfg(feature = "svg")]
use crate::core::animation::{Node, Signal};
use glam::Vec2;
use kurbo::Affine;
use std::sync::Arc;
use std::time::Duration;
use usvg::Tree;
#[cfg(feature = "runtime")]
use vello::Scene;
use crate::assets::svg_manager::SvgManager;
#[derive(Clone)]
pub struct SvgNode {
pub position: Signal<Vec2>,
pub rotation: Signal<f32>,
pub scale: Signal<Vec2>,
pub size: Signal<Vec2>,
pub tree: Option<Arc<Tree>>,
pub scene: Option<Arc<Scene>>,
pub opacity: Signal<f32>,
pub anchor: Signal<Vec2>,
pub path: String,
}
impl Default for SvgNode {
fn default() -> Self {
Self {
position: Signal::new(Vec2::ZERO),
rotation: Signal::new(0.0),
scale: Signal::new(Vec2::ONE),
size: Signal::new(Vec2::ZERO),
tree: None,
scene: None,
opacity: Signal::new(1.0),
anchor: Signal::new(Vec2::ZERO),
path: String::new(),
}
}
}
impl SvgNode {
pub fn new(pos: Vec2, path: &str) -> Self {
Self::default().with_position(pos).with_path(path)
}
pub fn with_position(mut self, position: Vec2) -> Self {
self.position = Signal::new(position);
self
}
pub fn with_rotation(mut self, angle: f32) -> Self {
self.rotation = Signal::new(angle);
self
}
pub fn with_scale(mut self, scale: f32) -> Self {
self.scale = Signal::new(Vec2::splat(scale));
self
}
pub fn with_scale_xy(mut self, scale: Vec2) -> Self {
self.scale = Signal::new(scale);
self
}
pub fn with_opacity(mut self, opacity: f32) -> Self {
self.opacity = Signal::new(opacity);
self
}
pub fn with_size(mut self, size: Vec2) -> Self {
self.size = Signal::new(size);
self
}
pub fn with_path(mut self, path: &str) -> Self {
self.path = path.to_string();
if let Some((tree, scene)) = SvgManager::get_svg(path) {
let size = tree.size();
self.size = Signal::new(Vec2::new(size.width() as f32, size.height() as f32));
self.tree = Some(tree);
self.scene = Some(scene);
}
self
}
pub fn with_anchor(mut self, anchor: Vec2) -> Self {
self.anchor = Signal::new(anchor);
self
}
}
impl Node for SvgNode {
#[cfg(feature = "runtime")]
fn render(&self, scene: &mut Scene, parent_transform: Affine, parent_opacity: f32) {
let (Some(ref tree), Some(ref svg_scene)) = (&self.tree, &self.scene) else {
return;
};
let size = self.size.get();
let pos = self.position.get();
let rot = self.rotation.get();
let sc = self.scale.get();
let anchor = self.anchor.get();
let bbox = tree.root().bounding_box();
let svg_w = bbox.width();
let svg_h = bbox.height();
let anchor_offset = (anchor + Vec2::new(1.0, 1.0)) * size * 0.5;
let local_transform = Affine::translate((pos.x as f64, pos.y as f64))
* Affine::rotate(rot as f64)
* Affine::scale_non_uniform(sc.x as f64, sc.y as f64)
* Affine::translate((-anchor_offset.x as f64, -anchor_offset.y as f64));
let opacity = self.opacity.get();
let final_opacity = opacity * parent_opacity;
if final_opacity <= 0.0 {
return;
}
let transform = parent_transform
* local_transform
* Affine::scale_non_uniform(size.x as f64 / svg_w as f64, size.y as f64 / svg_h as f64)
* Affine::translate((-bbox.left() as f64, -bbox.top() as f64));
scene.push_layer(
peniko::Mix::Normal,
final_opacity,
Affine::IDENTITY,
&kurbo::Rect::new(-1e10, -1e10, 1e10, 1e10),
);
scene.append(svg_scene, Some(transform));
scene.pop_layer();
}
fn update(&mut self, _dt: Duration) {}
fn state_hash(&self) -> u64 {
use crate::assets::hash::Hasher;
let mut h = Hasher::new();
h.update_bytes(self.path.as_bytes());
h.update_u64(self.position.state_hash());
h.update_u64(self.rotation.state_hash());
h.update_u64(self.scale.state_hash());
h.update_u64(self.size.state_hash());
h.update_u64(self.opacity.state_hash());
h.update_u64(self.anchor.state_hash());
h.finish()
}
fn clone_node(&self) -> Box<dyn Node> {
Box::new(self.clone())
}
fn reset(&mut self) {
self.position.reset();
self.rotation.reset();
self.scale.reset();
self.size.reset();
self.opacity.reset();
self.anchor.reset();
}
}