lottie: refactor trim object handling
[platform/core/uifw/lottie-player.git] / src / lottie / lottiemodel.h
1 #ifndef LOTModel_H
2 #define LOTModel_H
3
4 #include<vector>
5 #include<memory>
6 #include<unordered_map>
7 #include"vpoint.h"
8 #include"vrect.h"
9 #include"vinterpolator.h"
10 #include"vmatrix.h"
11 #include"vbezier.h"
12 #include"vbrush.h"
13 #include"vpath.h"
14
15 V_USE_NAMESPACE
16
17 class LOTCompositionData;
18 class LOTLayerData;
19 class LOTTransformData;
20 class LOTShapeGroupData;
21 class LOTShapeData;
22 class LOTRectData;
23 class LOTEllipseData;
24 class LOTTrimData;
25 class LOTRepeaterData;
26 class LOTFillData;
27 class LOTStrokeData;
28 class LOTGroupData;
29 class LOTGFillData;
30 class LOTGStrokeData;
31 class LottieShapeData;
32 class LOTPolystarData;
33 class LOTMaskData;
34
35 enum class MatteType
36 {
37     None = 0,
38     Alpha = 1,
39     AlphaInv,
40     Luma,
41     LumaInv
42 };
43
44 enum class LayerType {
45     Precomp = 0,
46     Solid = 1,
47     Image = 2,
48     Null = 3,
49     Shape = 4,
50     Text = 5
51 };
52
53 class LottieColor
54 {
55 public:
56     LottieColor() = default;
57     LottieColor(float red, float green , float blue):r(red), g(green),b(blue){}
58     VColor toColor(float a=1){ return VColor((255 * r), (255 * g), (255 * b), (255 * a));}
59     friend inline LottieColor operator+(const LottieColor &c1, const LottieColor &c2);
60     friend inline LottieColor operator-(const LottieColor &c1, const LottieColor &c2);
61 public:
62     float r{1};
63     float g{1};
64     float b{1};
65 };
66
67 inline LottieColor operator-(const LottieColor &c1, const LottieColor &c2)
68 {
69     return LottieColor(c1.r - c2.r, c1.g - c2.g, c1.b - c2.b);
70 }
71 inline LottieColor operator+(const LottieColor &c1, const LottieColor &c2)
72 {
73     return LottieColor(c1.r + c2.r, c1.g + c2.g, c1.b + c2.b);
74 }
75
76 inline const LottieColor operator*(const LottieColor &c, float m)
77 { return LottieColor(c.r*m, c.g*m, c.b*m); }
78
79 inline const LottieColor operator*(float m, const LottieColor &c)
80 { return LottieColor(c.r*m, c.g*m, c.b*m); }
81
82 class LottieShapeData
83 {
84 public:
85     void reserve(int size) {
86         mPoints.reserve(mPoints.size() + size);
87     }
88     void toPath(VPath& path) {
89         path.reset();
90
91         if (mPoints.empty()) return;
92
93         int size = mPoints.size();
94         const VPointF *points = mPoints.data();
95         /* reserve exact memory requirement at once
96          * ptSize = size + 1(size + close)
97          * elmSize = size/3 cubic + 1 move + 1 close
98          */
99         path.reserve(size + 1 , size/3 + 2);
100         path.moveTo(points[0]);
101         for (int i = 1 ; i < size; i+=3) {
102            path.cubicTo(points[i], points[i+1], points[i+2]);
103         }
104         if (mClosed)
105           path.close();
106     }
107 public:
108     std::vector<VPointF>    mPoints;
109     bool                     mClosed = false;   /* "c" */
110 };
111
112
113
114 template<typename T>
115 inline T lerp(const T& start, const T& end, float t)
116 {
117     return start + t * (end - start);
118 }
119
120 inline LottieShapeData lerp(const LottieShapeData& start, const LottieShapeData& end, float t)
121 {
122     if (start.mPoints.size() != start.mPoints.size())
123        return LottieShapeData();
124
125     LottieShapeData result;
126     result.reserve(start.mPoints.size());
127     for (unsigned int i = 0 ; i < start.mPoints.size(); i++) {
128        result.mPoints.push_back(start.mPoints[i] + t * (end.mPoints[i] - start.mPoints[i]));
129     }
130    return result;
131 }
132
133 template <typename T>
134 struct LOTKeyFrameValue
135 {
136     T mStartValue;
137     T mEndValue;
138     T value(float t) const {
139         return lerp(mStartValue, mEndValue, t);
140     }
141     float angle(float ) const { return 0;}
142 };
143
144 template <>
145 struct LOTKeyFrameValue<VPointF>
146 {
147     VPointF mStartValue;
148     VPointF mEndValue;
149     VPointF mInTangent;
150     VPointF mOutTangent;
151     bool    mPathKeyFrame = false;
152
153     VPointF value(float t) const {
154         if (mPathKeyFrame) {
155             /*
156              * position along the path calcualated
157              * using bezier at progress length (t * bezlen)
158              */
159             VBezier b = VBezier::fromPoints(mStartValue, mStartValue + mOutTangent,
160                                        mEndValue + mInTangent, mEndValue);
161             return b.pointAt(b.tAtLength(t * b.length()));
162
163         } else {
164             return lerp(mStartValue, mEndValue, t);
165         }
166     }
167
168     float angle(float t) const {
169         if (mPathKeyFrame) {
170             VBezier b = VBezier::fromPoints(mStartValue, mStartValue + mOutTangent,
171                                        mEndValue + mInTangent, mEndValue);
172             return b.angleAt(b.tAtLength(t * b.length()));
173         }
174         return 0;
175     }
176 };
177
178
179 template<typename T>
180 class LOTKeyFrame
181 {
182 public:
183     float progress(int frameNo) const {
184         return mInterpolator->value((frameNo - mStartFrame) / (mEndFrame - mStartFrame));
185     }
186     T value(int frameNo) const {
187         return mValue.value(progress(frameNo));
188     }
189     float angle(int frameNo) const {
190         return mValue.angle(progress(frameNo));
191     }
192
193 public:
194     float                 mStartFrame{0};
195     float                 mEndFrame{0};
196     std::shared_ptr<VInterpolator> mInterpolator;
197     LOTKeyFrameValue<T>  mValue;
198 };
199
200 template<typename T>
201 class LOTAnimInfo
202 {
203 public:
204     T value(int frameNo) const {
205         if (mKeyFrames.front().mStartFrame >= frameNo)
206             return mKeyFrames.front().mValue.mStartValue;
207         if(mKeyFrames.back().mEndFrame <= frameNo)
208             return mKeyFrames.back().mValue.mEndValue;
209
210         for(const auto &keyFrame : mKeyFrames) {
211             if (frameNo >= keyFrame.mStartFrame && frameNo < keyFrame.mEndFrame)
212                 return keyFrame.value(frameNo);
213         }
214         return T();
215     }
216
217     float angle(int frameNo) const {
218         if ((mKeyFrames.front().mStartFrame >= frameNo) ||
219             (mKeyFrames.back().mEndFrame <= frameNo) )
220             return 0;
221
222         for(const auto &keyFrame : mKeyFrames) {
223             if (frameNo >= keyFrame.mStartFrame && frameNo < keyFrame.mEndFrame)
224                 return keyFrame.angle(frameNo);
225         }
226         return 0;
227     }
228
229 public:
230     std::vector<LOTKeyFrame<T>>    mKeyFrames;
231 };
232
233 template<typename T>
234 class LOTAnimatable
235 {
236 public:
237     LOTAnimatable():mValue(),mAnimInfo(nullptr){}
238     LOTAnimatable(const T &value): mValue(value){}
239     bool isStatic() const {if (mAnimInfo) return false; else return true;}
240     T value(int frameNo) const {
241         return isStatic() ? mValue : mAnimInfo->value(frameNo);
242     }
243     float angle(int frameNo) const {
244         return isStatic() ? 0 : mAnimInfo->angle(frameNo);
245     }
246 public:
247     T                                 mValue;
248     int                               mPropertyIndex; /* "ix" */
249     std::unique_ptr<LOTAnimInfo<T>>   mAnimInfo;
250 };
251
252 enum class LottieBlendMode
253 {
254     Normal = 0,
255     Multiply = 1,
256     Screen = 2,
257     OverLay = 3
258 };
259
260 class LOTDataVisitor;
261 class LOTData
262 {
263 public:
264     enum class Type {
265         Composition = 1,
266         Layer,
267         ShapeGroup,
268         Transform,
269         Fill,
270         Stroke,
271         GFill,
272         GStroke,
273         Rect,
274         Ellipse,
275         Shape,
276         Polystar,
277         Trim,
278         Repeater
279     };
280     LOTData(LOTData::Type  type): mType(type){}
281     inline LOTData::Type type() const {return mType;}
282     bool isStatic() const{return mStatic;}
283     void setStatic(bool value) {mStatic = value;}
284     bool hidden() const {return mHidden;}
285 public:
286     bool                mStatic{true};
287     bool                mHidden{false};
288     LOTData::Type       mType;
289 };
290
291 class LOTGroupData: public LOTData
292 {
293 public:
294     LOTGroupData(LOTData::Type  type):LOTData(type){}
295 public:
296     std::vector<std::shared_ptr<LOTData>>  mChildren;
297     std::shared_ptr<LOTTransformData>      mTransform;
298 };
299
300 class LOTShapeGroupData : public LOTGroupData
301 {
302 public:
303     LOTShapeGroupData():LOTGroupData(LOTData::Type::ShapeGroup){}
304 };
305
306 class LOTLayerData;
307 struct LOTAsset
308 {
309     int                                          mAssetType; //lottie asset type  (precomp/char/image)
310     std::string                                  mRefId; // ref id
311     std::vector<std::shared_ptr<LOTData>>   mLayers;
312 };
313
314 class LOTLayerData : public LOTGroupData
315 {
316 public:
317     LOTLayerData():LOTGroupData(LOTData::Type::Layer){}
318     bool hasPathOperator() const noexcept {return mHasPathOperator;}
319     bool hasGradient() const noexcept {return mHasGradient;}
320     bool hasMask() const noexcept {return mHasMask;}
321     bool hasRepeater() const noexcept {return mHasRepeater;}
322     bool root() const noexcept {return mRoot;}
323     int id() const noexcept{ return mId;}
324     int parentId() const noexcept{ return mParentId;}
325     int inFrame() const noexcept{return mInFrame;}
326     int outFrame() const noexcept{return mOutFrame;}
327     int startFrame() const noexcept{return mStartFrame;}
328     int solidWidth() const noexcept{return mSolidLayer.mWidth;}
329     int solidHeight() const noexcept{return mSolidLayer.mHeight;}
330     LottieColor solidColor() const noexcept{return mSolidLayer.mColor;}
331     bool autoOrient() const noexcept{return mAutoOrient;}
332     int timeRemap(int frameNo) const;
333 public:
334     struct SolidLayer {
335         int            mWidth{0};
336         int            mHeight{0};
337         LottieColor    mColor;
338     };
339
340     MatteType            mMatteType{MatteType::None};
341     VRect                mBound;
342     LayerType            mLayerType; //lottie layer type  (solid/shape/precomp)
343     int                  mParentId{-1}; // Lottie the id of the parent in the composition
344     int                  mId{-1};  // Lottie the group id  used for parenting.
345     long                 mInFrame{0};
346     long                 mOutFrame{0};
347     long                 mStartFrame{0};
348     LottieBlendMode      mBlendMode;
349     float                mTimeStreatch{1.0f};
350     std::string          mPreCompRefId;
351     LOTAnimatable<float> mTimeRemap;  /* "tm" */
352     SolidLayer           mSolidLayer;
353     bool                 mHasPathOperator{false};
354     bool                 mHasMask{false};
355     bool                 mHasRepeater{false};
356     bool                 mHasGradient{false};
357     bool                 mRoot{false};
358     bool                 mAutoOrient{false};
359     std::vector<std::shared_ptr<LOTMaskData>>  mMasks;
360     LOTCompositionData   *mCompRef;
361 };
362
363 class LOTCompositionData : public LOTData
364 {
365 public:
366     LOTCompositionData():LOTData(LOTData::Type::Composition){}
367     double duration() const {
368         return isStatic() ? startFrame() :
369                             frameDuration() / frameRate(); // in second
370     }
371     size_t frameAtPos(double pos) const {
372         if (pos < 0) pos = 0;
373         if (pos > 1) pos = 1;
374         return isStatic() ? 0 : pos * frameDuration();
375     }
376     long frameAtTime(double timeInSec) const {
377         return isStatic() ? startFrame() : frameAtPos(timeInSec / duration());
378     }
379     long frameDuration() const {return mEndFrame - mStartFrame -1;}
380     float frameRate() const {return mFrameRate;}
381     long startFrame() const {return mStartFrame;}
382     long endFrame() const {return mEndFrame;}
383     VSize size() const {return mSize;}
384     void processRepeaterObjects();
385 public:
386     std::string          mVersion;
387     VSize                mSize;
388     long                 mStartFrame{0};
389     long                 mEndFrame{0};
390     float                mFrameRate;
391     LottieBlendMode      mBlendMode;
392     std::shared_ptr<LOTLayerData> mRootLayer;
393     std::unordered_map<std::string,
394                        std::shared_ptr<VInterpolator>> mInterpolatorCache;
395     std::unordered_map<std::string,
396                        std::shared_ptr<LOTAsset>>    mAssets;
397
398 };
399
400 /**
401  * TimeRemap has the value in time domain(in sec)
402  * To get the proper mapping first we get the mapped time at the current frame Number
403  * then we need to convert mapped time to frame number using the composition time line
404  * Ex: at frame 10 the mappend time is 0.5(500 ms) which will be convert to frame number
405  * 30 if the frame rate is 60. or will result to frame number 15 if the frame rate is 30.
406  */
407 inline int LOTLayerData::timeRemap(int frameNo) const
408 {
409     frameNo = mTimeRemap.isStatic() ? frameNo :
410               mCompRef->frameAtTime(mTimeRemap.value(frameNo));
411     /* Apply time streatch if it has any.
412      * Time streatch is just a factor by which the animation will speedup or slow
413      * down with respect to the overal animation.
414      * Time streach factor is already applied to the layers inFrame and outFrame.
415      * @TODO need to find out if timestreatch also affects the in and out frame of the
416      * child layers or not. */
417     return frameNo / mTimeStreatch;
418 }
419
420 struct LOT3DData
421 {
422     LOTAnimatable<float>     mRx{0};
423     LOTAnimatable<float>     mRy{0};
424     LOTAnimatable<float>     mRz{0};
425 };
426
427 class LOTTransformData : public LOTData
428 {
429 public:
430     LOTTransformData():LOTData(LOTData::Type::Transform),mScale({100, 100}){}
431     VMatrix matrix(int frameNo, bool autoOrient = false) const;
432     float    opacity(int frameNo) const;
433     void cacheMatrix();
434     bool staticMatrix() const {return mStaticMatrix;}
435     bool ddd() const {return m3D ? true : false;}
436 private:
437     VMatrix computeMatrix(int frameNo, bool autoOrient = false) const;
438 public:
439     std::unique_ptr<LOT3DData>    m3D;
440     LOTAnimatable<float>          mRotation{0};  /* "r" */
441     LOTAnimatable<VPointF>        mScale;     /* "s" */
442     LOTAnimatable<VPointF>        mPosition;  /* "p" */
443     LOTAnimatable<float>          mX{0};
444     LOTAnimatable<float>          mY{0};
445     LOTAnimatable<VPointF>        mAnchor;    /* "a" */
446     LOTAnimatable<float>          mOpacity{100};   /* "o" */
447     LOTAnimatable<float>          mSkew{0};      /* "sk" */
448     LOTAnimatable<float>          mSkewAxis{0};  /* "sa" */
449     bool                          mStaticMatrix{true};
450     bool                          mSeparate{false};
451     VMatrix                       mCachedMatrix;
452 };
453
454 class LOTFillData : public LOTData
455 {
456 public:
457     LOTFillData():LOTData(LOTData::Type::Fill){}
458     float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
459     FillRule fillRule() const {return mFillRule;}
460 public:
461     FillRule                       mFillRule{FillRule::Winding}; /* "r" */
462     LOTAnimatable<LottieColor>     mColor;   /* "c" */
463     LOTAnimatable<int>             mOpacity{100};  /* "o" */
464     bool                           mEnabled{true}; /* "fillEnabled" */
465 };
466
467 struct LOTDashProperty
468 {
469     LOTAnimatable<float>     mDashArray[5]; /* "d" "g" "o"*/
470     int                      mDashCount{0};
471     bool                     mStatic{true};
472 };
473
474 class LOTStrokeData : public LOTData
475 {
476 public:
477     LOTStrokeData():LOTData(LOTData::Type::Stroke){}
478     float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
479     float width(int frameNo) const {return mWidth.value(frameNo);}
480     CapStyle capStyle() const {return mCapStyle;}
481     JoinStyle joinStyle() const {return mJoinStyle;}
482     float meterLimit() const{return mMeterLimit;}
483     bool hasDashInfo() const { return !(mDash.mDashCount == 0);}
484     int getDashInfo(int frameNo, float *array) const;
485 public:
486     LOTAnimatable<LottieColor>        mColor;      /* "c" */
487     LOTAnimatable<int>                mOpacity{100};    /* "o" */
488     LOTAnimatable<float>              mWidth{0};      /* "w" */
489     CapStyle                          mCapStyle;   /* "lc" */
490     JoinStyle                         mJoinStyle;  /* "lj" */
491     float                             mMeterLimit{0}; /* "ml" */
492     LOTDashProperty                   mDash;
493     bool                              mEnabled{true}; /* "fillEnabled" */
494 };
495
496 class LottieGradient
497 {
498 public:
499     friend inline LottieGradient operator+(const LottieGradient &g1, const LottieGradient &g2);
500     friend inline LottieGradient operator-(const LottieGradient &g1, const LottieGradient &g2);
501     friend inline LottieGradient operator*(float m, const LottieGradient &g);
502 public:
503     std::vector<float>    mGradient;
504 };
505
506 inline LottieGradient operator+(const LottieGradient &g1, const LottieGradient &g2)
507 {
508     if (g1.mGradient.size() != g2.mGradient.size())
509         return g1;
510
511     LottieGradient newG;
512     newG.mGradient = g1.mGradient;
513
514     auto g2It = g2.mGradient.begin();
515     for(auto &i : newG.mGradient) {
516         i = i + *g2It;
517         g2It++;
518     }
519
520     return newG;
521 }
522
523 inline LottieGradient operator-(const LottieGradient &g1, const LottieGradient &g2)
524 {
525     if (g1.mGradient.size() != g2.mGradient.size())
526         return g1;
527     LottieGradient newG;
528     newG.mGradient = g1.mGradient;
529
530     auto g2It = g2.mGradient.begin();
531     for(auto &i : newG.mGradient) {
532         i = i - *g2It;
533         g2It++;
534     }
535
536     return newG;
537 }
538
539 inline LottieGradient operator*(float m, const LottieGradient &g)
540 {
541     LottieGradient newG;
542     newG.mGradient = g.mGradient;
543
544     for(auto &i : newG.mGradient) {
545         i = i * m;
546     }
547     return newG;
548 }
549
550
551
552 class LOTGradient : public LOTData
553 {
554 public:
555     LOTGradient(LOTData::Type  type):LOTData(type){}
556     inline float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
557     void update(std::unique_ptr<VGradient> &grad, int frameNo);
558
559 private:
560     void populate(VGradientStops &stops, int frameNo);
561 public:
562     int                                 mGradientType;        /* "t" Linear=1 , Radial = 2*/
563     LOTAnimatable<VPointF>              mStartPoint;          /* "s" */
564     LOTAnimatable<VPointF>              mEndPoint;            /* "e" */
565     LOTAnimatable<float>                mHighlightLength{0};     /* "h" */
566     LOTAnimatable<float>                mHighlightAngle{0};      /* "a" */
567     LOTAnimatable<int>                  mOpacity{0};             /* "o" */
568     LOTAnimatable<LottieGradient>       mGradient;            /* "g" */
569     int                                 mColorPoints{-1};
570     bool                                mEnabled{true};      /* "fillEnabled" */
571 };
572
573 class LOTGFillData : public LOTGradient
574 {
575 public:
576     LOTGFillData():LOTGradient(LOTData::Type::GFill){}
577     FillRule fillRule() const {return mFillRule;}
578 public:
579     FillRule                       mFillRule{FillRule::Winding}; /* "r" */
580 };
581
582 class LOTGStrokeData : public LOTGradient
583 {
584 public:
585     LOTGStrokeData():LOTGradient(LOTData::Type::GStroke){}
586     float width(int frameNo) const {return mWidth.value(frameNo);}
587     CapStyle capStyle() const {return mCapStyle;}
588     JoinStyle joinStyle() const {return mJoinStyle;}
589     float meterLimit() const{return mMeterLimit;}
590     bool hasDashInfo() const { return !(mDash.mDashCount == 0);}
591     int getDashInfo(int frameNo, float *array) const;
592 public:
593     LOTAnimatable<float>           mWidth;       /* "w" */
594     CapStyle                       mCapStyle;    /* "lc" */
595     JoinStyle                      mJoinStyle;   /* "lj" */
596     float                          mMeterLimit{0};  /* "ml" */
597     LOTDashProperty                mDash;
598 };
599
600 class LOTPath : public LOTData
601 {
602 public:
603     LOTPath(LOTData::Type  type):LOTData(type){}
604     VPath::Direction direction() { if (mDirection == 3) return VPath::Direction::CCW;
605                                    else return VPath::Direction::CW;}
606 public:
607     int                                    mDirection{1};
608 };
609
610 class LOTShapeData : public LOTPath
611 {
612 public:
613     LOTShapeData():LOTPath(LOTData::Type::Shape){}
614     void process();
615 public:
616     LOTAnimatable<LottieShapeData>    mShape;
617 };
618
619 class LOTMaskData
620 {
621 public:
622     enum class Mode {
623       None,
624       Add,
625       Substarct,
626       Intersect,
627       Difference
628     };
629     float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
630     bool isStatic() const {return mIsStatic;}
631 public:
632     LOTAnimatable<LottieShapeData>    mShape;
633     LOTAnimatable<float>              mOpacity;
634     bool                              mInv{false};
635     bool                              mIsStatic{true};
636     LOTMaskData::Mode                 mMode;
637 };
638
639 class LOTRectData : public LOTPath
640 {
641 public:
642     LOTRectData():LOTPath(LOTData::Type::Rect){}
643 public:
644     LOTAnimatable<VPointF>    mPos;
645     LOTAnimatable<VPointF>    mSize;
646     LOTAnimatable<float>      mRound{0};
647 };
648
649 class LOTEllipseData : public LOTPath
650 {
651 public:
652     LOTEllipseData():LOTPath(LOTData::Type::Ellipse){}
653 public:
654     LOTAnimatable<VPointF>   mPos;
655     LOTAnimatable<VPointF>   mSize;
656 };
657
658 class LOTPolystarData : public LOTPath
659 {
660 public:
661     enum class PolyType {
662         Star = 1,
663         Polygon = 2
664     };
665     LOTPolystarData():LOTPath(LOTData::Type::Polystar){}
666 public:
667     LOTPolystarData::PolyType     mType{PolyType::Polygon};
668     LOTAnimatable<VPointF>        mPos;
669     LOTAnimatable<float>          mPointCount{0};
670     LOTAnimatable<float>          mInnerRadius{0};
671     LOTAnimatable<float>          mOuterRadius{0};
672     LOTAnimatable<float>          mInnerRoundness{0};
673     LOTAnimatable<float>          mOuterRoundness{0};
674     LOTAnimatable<float>          mRotation{0};
675 };
676
677 class LOTTrimData : public LOTData
678 {
679 public:
680     struct Segment {
681         float start{0};
682         float end{0};
683     };
684     enum class TrimType {
685         Simultaneously,
686         Individually
687     };
688     LOTTrimData():LOTData(LOTData::Type::Trim){}
689     Segment segment(int frameNo) const {
690         float start = mStart.value(frameNo)/100.0f;
691         float end = mEnd.value(frameNo)/100.0f;
692         float offset = fmod(mOffset.value(frameNo), 360.0f)/ 360.0f;
693         start += offset;
694         end += offset;
695         // normalize start, end value to 0 - 1
696         if (fabs(start) > 1) start = fmod(start, 1);
697         if (fabs(end) > 1) end = fmod(end, 1);
698         Segment s;
699         if (start >= 0 && end >= 0) {
700             s.start = std::min(start, end);
701             s.end = std::max(start, end);
702         } else if (start < 0 && end < 0) {
703             start += 1;
704             end +=1;
705             s.start = std::min(start, end);
706             s.end = std::max(start, end);
707         } else {
708             // one of them is -ve so it a loop
709             if (start < 0) start +=1;
710             if (end < 0) end +=1;
711             s.start = std::max(start, end);
712             s.end = std::min(start, end);
713         }
714         return s;
715
716     }
717     LOTTrimData::TrimType type() const {return mTrimType;}
718 public:
719     LOTAnimatable<float>             mStart{0};
720     LOTAnimatable<float>             mEnd{0};
721     LOTAnimatable<float>             mOffset{0};
722     LOTTrimData::TrimType            mTrimType{TrimType::Simultaneously};
723 };
724
725 class LOTRepeaterData : public LOTGroupData
726 {
727 public:
728     LOTRepeaterData():LOTGroupData(LOTData::Type::Repeater){}
729 public:
730     LOTAnimatable<float>             mCopies{0};
731     LOTAnimatable<float>             mOffset{0};
732 };
733
734 class LOTModel
735 {
736 public:
737    bool  isStatic() const {return mRoot->isStatic();}
738    double duration() const {return mRoot->duration();}
739    size_t frameDuration() const {return mRoot->frameDuration();}
740    size_t frameRate() const {return mRoot->frameRate();}
741    size_t startFrame() const {return mRoot->startFrame();}
742    size_t endFrame() const {return mRoot->endFrame();}
743    size_t frameAtPos(double pos) const {return mRoot->frameAtPos(pos);}
744 public:
745     std::shared_ptr<LOTCompositionData> mRoot;
746 };
747
748 #endif // LOTModel_H