1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/animation/friction_motion.h"
17 
18 #include "base/utils/utils.h"
19 
20 namespace OHOS::Ace {
21 namespace {
22 
23 constexpr float UNIT_CONVERT = 1000.0f;
24 constexpr float FRICTION_SCALE = -4.2f;
25 constexpr float DEFAULT_THRESHOLD = 0.75f;
26 
27 } // namespace
28 
FrictionMotion(double friction,double initPosition,double initVelocity)29 FrictionMotion::FrictionMotion(double friction, double initPosition, double initVelocity)
30 {
31     Reset(friction, initPosition, initVelocity);
32 }
33 
Reset(double friction,double initPosition,double initVelocity,double threshold)34 void FrictionMotion::Reset(double friction, double initPosition, double initVelocity, double threshold)
35 {
36     if (!IsValid(friction)) {
37         return;
38     }
39     friction_ = friction * FRICTION_SCALE;
40     initVelocity_ = std::abs(initVelocity);
41     currentTime_ = 0.0;
42     initPosition_ = initPosition;
43     valueThreshold_ = DEFAULT_THRESHOLD;
44     velocityThreshold_ = valueThreshold_ * threshold;
45 
46     if (NearZero(initVelocity_)) {
47         signum_ = 0.0;
48         finalTime_ = 0.0;
49     } else {
50         signum_ = GreatNotEqual(initVelocity, 0.0) ? 1.0 : -1.0;
51         finalTime_ = UNIT_CONVERT * std::log(velocityThreshold_ / initVelocity_) / friction_;
52     }
53     finalTime_ = std::max(finalTime_, 0.0);
54     finalPosition_ = GetPosition(finalTime_ / UNIT_CONVERT);
55 }
56 
IsValid(double friction) const57 bool FrictionMotion::IsValid(double friction) const
58 {
59     if (friction < 0.0 || NearZero(friction)) {
60         LOGE("Invalid friction:%{public}lf.", friction);
61         return false;
62     }
63     return true;
64 }
65 
Move(float offsetTime)66 void FrictionMotion::Move(float offsetTime)
67 {
68     // change millisecond to second.
69     currentTime_ = offsetTime / UNIT_CONVERT;
70 }
71 
GetPosition(float offsetTime) const72 double FrictionMotion::GetPosition(float offsetTime) const
73 {
74     return initPosition_ + signum_ * (initVelocity_ / friction_) * std::expm1(friction_ * offsetTime);
75 }
76 
GetVelocityByFinalPosition(double final,double threshold) const77 double FrictionMotion::GetVelocityByFinalPosition(double final, double threshold) const
78 {
79     return  valueThreshold_ * threshold * signum_ - (final - initPosition_) * friction_;
80 }
81 
GetVelocity(float offsetTime) const82 double FrictionMotion::GetVelocity(float offsetTime) const
83 {
84     return signum_ * initVelocity_ * std::exp(friction_ * offsetTime);
85 }
86 
GetCurrentPosition()87 double FrictionMotion::GetCurrentPosition()
88 {
89     return GetPosition(currentTime_);
90 }
91 
GetCurrentVelocity()92 double FrictionMotion::GetCurrentVelocity()
93 {
94     return GetVelocity(currentTime_);
95 }
96 
IsCompleted()97 bool FrictionMotion::IsCompleted()
98 {
99     return NearZero(GetCurrentVelocity(), velocityThreshold_) ||
100         NearEqual(finalPosition_, GetCurrentPosition(), 1.0);
101 }
102 
GetFinalPosition() const103 double FrictionMotion::GetFinalPosition() const
104 {
105     return finalPosition_;
106 }
107 
GetTimeByPosition(double position,double & time) const108 bool FrictionMotion::GetTimeByPosition(double position, double& time) const
109 {
110     time = 0.0;
111     if (NearZero(initVelocity_)) {
112         return false;
113     }
114 
115     double rangeStart = 0.0;
116     double rangeEnd = finalPosition_;
117     if (finalPosition_ < 0.0) {
118         rangeStart = finalPosition_;
119         rangeEnd = 0.0;
120     }
121     if (position < rangeStart || position > rangeEnd) {
122         return false;
123     }
124     // Deduced by formula of Func(GetPosition)
125     time = std::log(position * friction_ / initVelocity_ + 1.0) / friction_;
126     return true;
127 }
128 
GetMotionType() const129 std::string FrictionMotion::GetMotionType() const
130 {
131     return "friction";
132 }
133 
134 } // namespace OHOS::Ace
135