facebook开源卡通片引擎 pop 源码阅读笔记

facebook开源动画引擎 pop 源码阅读笔记:

facebook开源动画引擎 pop源码阅读笔记:

初识POP:http://geek.****.net/news/detail/7534

POP官网:https://github.com/facebook/pop

demo:https://github.com/jxd001/POPdemo/blob/master/README.md

转载请注明出处:facebook开源动画引擎 pop源码阅读笔记:


1.代码结构:
代码包括四个目录:

Animations: 定义pop支持的动画类型,并抽像各种动画的低层数据结构
Engine:组织动化运行的数据结构,核心是动化管理者,还包括了动画引擎所需要的可动画属性定义、动画追踪等内容
Utility: 封装Engine中用到的各种功能,包括数值运算,数据转换,运算等
WebCore: 这部分是苹果公司的代码,矩阵运算和贝赛尔曲线的,用于底层运算,非常值得学习。

各层关键类的功能注释:
a.Animations
    POPAnimation
:定义了动画基类POPAnimation
        私有变量:结构体_POPAnimationState 纪录动画状态,是实现动画最关键的属性
        公有变量:name beginTime delegate tracer(动画追踪,纪录各种动画事件) ^completionBlock removedOnCompletion paused

    继承关系:
    NSObject
    POPAnimation
        POPPropertyAnimation
            POPBasicAnimation
            POPDecayAnimation
            POPSpringAnimation
        POPCustomAnimation

    _POPAnimationState
        _POPPropertyAnimationState
            _POPBasicAnimationState
            _POPDecayAnimationState
            _POPSpringAnimationState


    NSObject (POP)让所有对象增加pop动画支持(增删改查)通过POPAnimator进行管理


b.Engine
    POPAnimateableProperty
可动画的属性,用于定义动画类型和动画时修改值

        POPStaticAnimatablePropertyState 结构体纪录了属性名,readBlock,writeBlock,和域值

        79行 POPStaticAnimatablePropertyState _staticStates[]  纪录了所有可动画的属性对应的名称,其中的writeblock功能是把二进制数据写到对应的属性里

    类族:

    POPAnimatableProperty
        POPConcreteAnimatableProperty
        POPMutableAnimatableProperty
        POPPlaceholderAnimatableProperty
        POPStaticAnimatableProperty

        
    POPAnimationEvent POPAnimationTracer的基本元素

    POPAnimationExtras 扩展CAAnimation 和POPSpringAnimation 的方法(分类)

    POPAnimationRuntime 这里有几个函数比较特殊
        POPBox 包装一个函数vector,把vector转化为point,size,rect,color等
        POPUnbox 解包一个向量,把point,size,rect,color对象转换为vector

    POPAnimationTracer 动画跟踪 通过POPAnimationEvent来描述

    POPAnimator 动画管理者,纪录所有的动画事件(POPAnimatorItem,纪录了动画作用到的对象、动画key、动画对象等信息),初始化是把CADisplayLink注册到runloop里定时触发render过程,进行一帧动画渲染。她提供了add、remove等操作Animation集合的方法,并提供了Observer和delegate等事件函数
    POPAnimator通过两层(key,view)存储动画,(动画目标对象,(动画key,动画对象))
    具体每帧的渲染过程有两步:1.renderTime,更新state的状态。 2.updateAnimatable,把state的变化更新到obj的具体属性上

c.Utility 各种工具类编写
    POPAction: ActionDisabler和ActionEnabler  利用RAII暂停核心动画以ActionEnabler为例,创建ActionEnabler时停止动画,销毁时重新运行动画

    POPCGUtils:颜色转换函数,数组和点,point,size,rect,color等转换函数

    POPGeometry:扩展NSValue到POP自定义类型的转换

    POPLayerExtras:图层矩阵变换封装,底层由TransformationMatrix实现

    POPMath:数学计算接口,封装了UnitBezier,POPVector和其他数字计算

    SpringSolver:spring插值计算

    POPVector:向量运算

d.WebCore
    FloatConversion
:浮点数转换方法,提供double->float和double->CGFloat,
    用途:服务于矩阵运算

    TransformationMatrix:矩阵运算,包括初始化,scale,rotate,translate,flipX,flipY,skew,applyPerspective???,multVecMatrix向量和矩阵相乘等矩阵操作,
    备注:以后详细看:
    CATransform3D 矩阵变换之立方体旋转实现细节 http://blog.****.net/ch_soft/article/details/7351896
    矩阵的三维变换(转)http://www.cnblogs.com/Clingingboy/archive/2010/10/17/1853566.html
    关键方法说明:
    计算行列式的值:不列举了,很枯燥,行列式计算的基本概念,从二阶计算到三阶再计算到四阶。主要作用是引擎层对layer进行坐标变换

    UnitBezier: 结构体,初始化参数为两个控制点p1(p1x,p1y),p2(p2x,p2y),用于表示起始点为s(0,0),终止点为e(1,1),p1为第一控制点,p2为第二控制点的二次Bezier。
    用途:重新实现系统的自带动画
    备注:UnitBezier曲线相关知识:http://blog.****.net/jimi36/article/details/7792103
    方法说明:
    UnitBezier(double p1x, double p1y, double p2x, double p2y)//用两个控制点的坐标初始化曲线,
    double sampleCurveX(double t)//通过参数t计算对应的x值
    double sampleCurveY(double t)//通过参数t计算对应的y值
    double sampleCurveDerivativeX(double t)
    //epsilon表示一个小间距,相当于数学计算里的dX,一般为全距离的1/1000,
    //求解x对应的t值,用到了函数sampleCurveDerivativeX
    double solveCurveX(double x, double epsilon)
    double solve(double x, double epsilon)//求解x对应的y值,分为两步算,1.由x计算t;2.由t计算y

2.动画执行流程
    1)POPAnimator是单例对象,初始化对象实例时注册把CADisplayLink到runloop,定时调用render,通过renderTime函数遍历所有正在执行的动画,使动画进行到下一帧,并刷新画面,直到所有动画推进完成。
    2)动画状态的推进最终都是通过_POPAnimationState类和其子类的advance方法推进的,advance更新时间戳,updateAnimatable方法把state的状态变更到动画要作用的obj对象。
    3)Spring、Decay、Basic 都有各自实现的advance函数计算,而Custom方式不同,他是通过一个POPCustomAnimationBlock类型的回调来就算这一帧的值的。

3.动画算法
不同动画的advance都是如何推进的呢?
Basic:动画通过起点为(0,0),终点为(1,1)的Bezier曲线计算,x轴为时间,y轴为比例。advance方法中需要根据t值计算出对应的y值p,然后根据p的值进行插值计算(v=s+(e-s)*p),并进行了按照最小精度的四舍五入(源码设置的精度是 1/(1000*总时间) ,动画时间越长,精度越高)。

Spring:这里的Vector4d不是真的向量,它可以表示point,size等结构,它起始就是个属性的数组而已,不要去想它的几何意义。
这个模型其实是一个简谐运动,
计算过程类似于弹簧,计算主要使用到这样几个量:
 |---------|------|------ 轨迹
 起点             当前点   间隔  平衡位置
p,表示当前点到弹簧平衡位置的距离   v,表示振子的速度   当前时间t    间隔时间dt
k弹簧的 劲度系数 f= kl     b阻力系数     m质量    
    _tp 瞬时值(偏移量)
    _tv 瞬时速度
    _ta 瞬时加速的
    Derivative 导数~~
    利用这些参数做插值算法,算出下一时刻的p和v
    计算原理大致就是 p2 = p + dt * v(因为v=p');v2 = v + dt * v'。
    计算过程中运用了插值算法减小误差。说实话,没太看懂。希望这方面专家补充下。
    问题:程序中计算dv用的是state.p*(-_k/_m) - state.v*(_b/_m);
    正常dv=加速度a=f/m=f牵引/m-f阻力/m = state.p*(-_k/_m) - 阻力/m   这里库的作者可能认为阻力和速度成正比~~~,高中物理学的阻力是恒定的,但是模型运行的效果确实不错。
    ps:看到这里让我很后悔上学时没好好学习计算方法,如果多会一点计算方法,这里应该能看的稍微明白点,懂得大牛指点下

Decay:衰减函数,没有说明 不知道为什么,应该和衰减公式有点关系,没弄清楚怎么变形的,下边是源码中的注释,v表示速度,dt时间间隔x位移
    // v0 = v / 1000
    // v = v0 * powf(deceleration, dt);
    // v = v * 1000;
    // x0 = x;
    // x = x0 + v0 * deceleration * (1 - powf(deceleration, dt)) / (1 - deceleration)

Custom:通过自定义block刷新动画帧,更灵活的方式


后续问题:
1.没弄明白衰减公式的原理,希望数学物理大牛不吝赐教,期待学到这方面的知识。
2.Custom具体没试过,自由度应该很大,下次做做demo试试。
3.很多细节调用没有看懂,还有很多值得学习的地方