use std::any::{self, Any, TypeId};
use std::fmt;
use std::rc::Rc;
use crate::animation::Loop;
use crate::artboard::Artboard;
use crate::draw_target::DrawTargetPlacement;
use crate::importers::ImportStack;
use crate::shapes::paint::{BlendMode, Color32, StrokeCap, StrokeJoin};
use crate::shapes::FillRule;
use crate::StatusCode;
mod binary_reader;
mod object;
mod property;
pub use binary_reader::BinaryReader;
pub use object::{Object, ObjectRef};
pub use property::{Property, TryFromU64};
macro_rules! types {
( $( ( $id:expr , $type:ty ) ),* $( , )? ) => {
pub fn get_type_id(id: u16) -> Option<TypeId> {
match id {
$(
$id => Some(TypeId::of::<$type>()),
)*
_ => None,
}
}
impl dyn Core {
pub fn from_type_id(id: TypeId) -> Option<(Rc<dyn Core>, Object)> {
match id {
$(
id if id == TypeId::of::<$type>() => {
let rc: Rc<dyn Core> = Rc::new(<$type>::default());
let object = Object::<$type>::new(&rc);
Some((rc, object.into()))
}
)*
_ => None,
}
}
}
};
}
macro_rules! parent_types {
( ( $field_head:ident , $type_head:ty ) $( , ( $field_tail:ident , $type_tail:ty ) )* $( , )? ) => {
fn ref_of(&self, id: ::std::any::TypeId) -> Option<&dyn Core> {
if id == ::std::any::TypeId::of::<$type_head>() {
Some(&self.$field_head)
}
$(
else if id == ::std::any::TypeId::of::<$type_tail>() {
Some(&self.$field_tail)
}
)*
else {
self.$field_head
.ref_of(id)
$(
.or_else(|| self.$field_tail.ref_of(id))
)*
}
}
};
}
macro_rules! properties {
( $parent:ident ) => {
fn property_of(&self, key: u16) -> Option<&dyn ::std::any::Any> {
self.$parent.property_of(key)
}
fn animate(&self, object: &ObjectRef<'_>, key: u64, animator: &dyn ::std::any::Any) {
self.$parent.animate(object, key, animator);
}
};
( $( ( $key:expr , $field:ident , $setter:ident ) ),* , $parent:ident $( , )? ) => {
fn property_of(&self, key: u16) -> Option<&dyn ::std::any::Any> {
match key {
$(
$key => Some(&self.$field),
)*
_ => self.$parent.property_of(key),
}
}
fn animate(&self, object: &ObjectRef<'_>, key: u64, animator: &dyn ::std::any::Any) {
$(
if key == $key {
if let Some(animator) = animator.downcast_ref::<crate::animation::Animator<_>>() {
return animator.animate(&object.cast(), ObjectRef::<Self>::$setter);
}
}
)*
self.$parent.animate(&object, key, animator);
}
};
( $( ( $key:expr , $field:ident , $setter:ident ) ),* $( , )? ) => {
fn property_of(&self, key: u16) -> Option<&dyn ::std::any::Any> {
match key {
$(
$key => Some(&self.$field),
)*
_ => None,
}
}
fn animate(&self, object: &ObjectRef<'_>, key: u64, animator: &dyn ::std::any::Any) {
$(
if key == $key {
if let Some(animator) = animator.downcast_ref::<crate::animation::Animator<_>>() {
return animator.animate(&object.cast(), ObjectRef::<Self>::$setter);
}
}
)*
}
};
}
macro_rules! on_added {
( @impl import ) => {
fn import(&self, _object: crate::core::Object, _import_stack: &crate::importers::ImportStack) -> crate::status_code::StatusCode {
crate::status_code::StatusCode::Ok
}
};
( @impl $method:ident ) => {
fn $method(&self, _context: &dyn crate::core::CoreContext) -> crate::status_code::StatusCode {
crate::status_code::StatusCode::Ok
}
};
( @impl import , $type:ty ) => {
fn import(&self, object: crate::core::Object, import_stack: &crate::importers::ImportStack) -> crate::status_code::StatusCode {
self.cast::<$type>().import(object, import_stack)
}
};
( @impl $method:ident , $type:ty ) => {
fn $method(&self, context: &dyn crate::core::CoreContext) -> crate::status_code::StatusCode {
self.cast::<$type>().$method(context)
}
};
() => {
on_added!(@impl on_added_dirty);
on_added!(@impl on_added_clean);
on_added!(@impl import);
};
( [ $( $method:ident ),+ ] ) => {
$(
on_added!(@impl $method);
)+
};
( [ $( $method:ident ),+ ] , $type:ty ) => {
$(
on_added!(@impl $method, $type);
)+
};
( $type:ty ) => {
on_added!(@impl on_added_dirty, $type);
on_added!(@impl on_added_clean, $type);
on_added!(@impl import, $type);
};
}
macro_rules! match_cast {
( $val:expr , { $( $to:ident ( $name:ident ) => $block:expr ),* $( , _ => $default:expr )? $( , )? } ) => {{
let f = || {
$(
if let Some($name) = $val.try_cast::<$to>() {
return $block;
}
)*
$($default)?
};
f()
}};
}
pub trait AsAny: Any + fmt::Debug {
fn as_any(&self) -> &dyn Any;
fn any_type_name(&self) -> &'static str;
}
impl<T: Any + fmt::Debug> AsAny for T {
#[inline(always)]
fn as_any(&self) -> &dyn Any {
self
}
#[inline(always)]
fn any_type_name(&self) -> &'static str {
core::any::type_name::<T>()
}
}
trait Cast: Core {
#[inline]
fn is<T>(&self) -> bool
where
T: Core,
{
self.as_any().is::<T>() || self.ref_of(TypeId::of::<T>()).is_some()
}
#[inline]
fn try_cast<T>(&self) -> Option<&T>
where
T: Core,
{
self.as_any()
.downcast_ref()
.or_else(|| self.ref_of(TypeId::of::<T>()).and_then(|any| any.as_any().downcast_ref()))
}
#[inline]
fn cast<T>(&self) -> &T
where
T: Core,
{
self.try_cast().unwrap_or_else(|| panic!("failed cast to {}", any::type_name::<T>()))
}
}
impl Cast for dyn Core {}
#[allow(unused_variables)]
pub trait Core: AsAny {
#[inline]
fn ref_of(&self, id: TypeId) -> Option<&dyn Core> {
None
}
#[inline]
fn property_of(&self, key: u16) -> Option<&dyn Any> {
None
}
#[inline]
fn animate(&self, object: &ObjectRef<'_>, key: u64, animator: &dyn Any) {}
#[inline]
fn type_name(&self) -> &'static str {
self.any_type_name().rsplit("::").next().unwrap()
}
}
impl dyn Core {
#[inline]
pub fn get_property<T: Clone + Default + 'static>(&self, key: u16) -> Option<&Property<T>> {
self.property_of(key).and_then(<dyn Any>::downcast_ref::<Property<T>>)
}
pub(crate) fn write(&self, property_key: u16, reader: &mut BinaryReader<'_>) -> bool {
if let Some(property) = self.property_of(property_key) {
macro_rules! write_types {
( $property:expr , $reader:expr , [ $( $type:ident ),* $( , )? ] ) => {
match $property.type_id() {
$(
id if id == TypeId::of::<Property<$type>>() => {
let property = property.downcast_ref::<Property<$type>>().unwrap();
return $reader.write(property);
}
)*
_ => (),
}
};
}
write_types!(
property,
reader,
[
bool,
u32,
u64,
f32,
String,
Color32,
BlendMode,
DrawTargetPlacement,
FillRule,
Loop,
StrokeCap,
StrokeJoin,
]
);
}
false
}
}
pub trait OnAdded {
fn on_added_dirty(&self, context: &dyn CoreContext) -> StatusCode;
fn on_added_clean(&self, context: &dyn CoreContext) -> StatusCode;
fn import(&self, object: Object, import_stack: &ImportStack) -> StatusCode;
}
pub trait CoreContext {
fn resolve(&self, id: usize) -> Option<Object>;
fn artboard(&self) -> Object<Artboard> {
self.resolve(0)
.and_then(|object| object.try_cast())
.expect("frist object should always be the Artboard")
}
}
types![
(1, crate::Artboard),
(2, crate::Node),
(3, crate::shapes::Shape),
(4, crate::shapes::Ellipse),
(5, crate::shapes::StraightVertex),
(6, crate::shapes::CubicDetachedVertex),
(7, crate::shapes::Rectangle),
(8, crate::shapes::Triangle),
(9, crate::shapes::PathComposer),
(10, crate::Component),
(11, crate::ContainerComponent),
(13, crate::Drawable),
(14, crate::shapes::PathVertex),
(15, crate::shapes::ParametricPath),
(16, crate::shapes::PointsPath),
(17, crate::shapes::paint::RadialGradient),
(18, crate::shapes::paint::SolidColor),
(19, crate::shapes::paint::GradientStop),
(20, crate::shapes::paint::Fill),
(21, crate::shapes::paint::ShapePaint),
(22, crate::shapes::paint::LinearGradient),
(23, crate::Backboard),
(24, crate::shapes::paint::Stroke),
(25, crate::animation::KeyedObject),
(26, crate::animation::KeyedProperty),
(27, crate::animation::Animation),
(28, crate::animation::CubicInterpolator),
(29, crate::animation::KeyFrame),
(30, crate::animation::KeyFrameDouble),
(31, crate::animation::LinearAnimation),
(34, crate::shapes::CubicAsymmetricVertex),
(35, crate::shapes::CubicMirroredVertex),
(37, crate::animation::KeyFrameColor),
(38, crate::TransformComponent),
(39, crate::bones::SkeletalComponent),
(40, crate::bones::Bone),
(41, crate::bones::RootBone),
(42, crate::shapes::ClippingShape),
(43, crate::bones::Skin),
(44, crate::bones::Tendon),
(45, crate::bones::Weight),
(46, crate::bones::CubicWeight),
(47, crate::shapes::paint::TrimPath),
(48, crate::DrawTarget),
(49, crate::DrawRules),
(50, crate::animation::KeyFrameId),
(51, crate::shapes::Polygon),
(52, crate::shapes::Star),
(53, crate::animation::StateMachine),
(54, crate::animation::StateMachineComponent),
(55, crate::animation::StateMachineInput),
(56, crate::animation::StateMachineDouble),
(57, crate::animation::StateMachineLayer),
(58, crate::animation::StateMachineTrigger),
(59, crate::animation::StateMachineBool),
(60, crate::animation::LayerState),
(61, crate::animation::AnimationState),
(62, crate::animation::AnyState),
(63, crate::animation::EntryState),
(64, crate::animation::ExitState),
(65, crate::animation::StateTransition),
(66, crate::animation::StateMachineLayerComponent),
(67, crate::animation::TransitionCondition),
(68, crate::animation::TransitionTriggerCondition),
(69, crate::animation::TransitionValueCondition),
(70, crate::animation::TransitionDoubleCondition),
(71, crate::animation::TransitionBoolCondition),
];