18bf80f4bSopenharmony_ci/*
28bf80f4bSopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
38bf80f4bSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
48bf80f4bSopenharmony_ci * you may not use this file except in compliance with the License.
58bf80f4bSopenharmony_ci * You may obtain a copy of the License at
68bf80f4bSopenharmony_ci *
78bf80f4bSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
88bf80f4bSopenharmony_ci *
98bf80f4bSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
108bf80f4bSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
118bf80f4bSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
128bf80f4bSopenharmony_ci * See the License for the specific language governing permissions and
138bf80f4bSopenharmony_ci * limitations under the License.
148bf80f4bSopenharmony_ci */
158bf80f4bSopenharmony_ci#include "bezier_curve.h"
168bf80f4bSopenharmony_ci
178bf80f4bSopenharmony_ci#include <base/math/mathf.h>
188bf80f4bSopenharmony_ci
198bf80f4bSopenharmony_ci#include <meta/api/make_callback.h>
208bf80f4bSopenharmony_ci#include <meta/api/util.h>
218bf80f4bSopenharmony_ci#include <meta/interface/property/property_events.h>
228bf80f4bSopenharmony_ci
238bf80f4bSopenharmony_ciMETA_BEGIN_NAMESPACE()
248bf80f4bSopenharmony_ci
258bf80f4bSopenharmony_cinamespace Curves {
268bf80f4bSopenharmony_cinamespace Easing {
278bf80f4bSopenharmony_ci
288bf80f4bSopenharmony_cibool CubicBezierEasingCurve::Build(const IMetadata::Ptr& meta)
298bf80f4bSopenharmony_ci{
308bf80f4bSopenharmony_ci    if (Super::Build(meta)) {
318bf80f4bSopenharmony_ci        auto update = MakeCallback<IOnChanged>(this, &CubicBezierEasingCurve::UpdateCoefficients);
328bf80f4bSopenharmony_ci        META_ACCESS_PROPERTY(ControlPoint1)->OnChanged()->AddHandler(update);
338bf80f4bSopenharmony_ci        META_ACCESS_PROPERTY(ControlPoint2)->OnChanged()->AddHandler(update);
348bf80f4bSopenharmony_ci        UpdateCoefficients();
358bf80f4bSopenharmony_ci        return true;
368bf80f4bSopenharmony_ci    }
378bf80f4bSopenharmony_ci    return false;
388bf80f4bSopenharmony_ci}
398bf80f4bSopenharmony_ci
408bf80f4bSopenharmony_cifloat CubicBezierEasingCurve::Transform(float t) const
418bf80f4bSopenharmony_ci{
428bf80f4bSopenharmony_ci    // First, solve "linear" X coordinate for t [0..1], then sample the bezier's Y at that X.
438bf80f4bSopenharmony_ci    return GetY(GetLinearX(t));
448bf80f4bSopenharmony_ci}
458bf80f4bSopenharmony_ci
468bf80f4bSopenharmony_civoid CubicBezierEasingCurve::UpdateCoefficients()
478bf80f4bSopenharmony_ci{
488bf80f4bSopenharmony_ci    auto cp1 = META_NS::GetValue(META_ACCESS_PROPERTY(ControlPoint1));
498bf80f4bSopenharmony_ci    auto cp2 = META_NS::GetValue(META_ACCESS_PROPERTY(ControlPoint2));
508bf80f4bSopenharmony_ci
518bf80f4bSopenharmony_ci    // Clamp x-coordinates of our control points between [0,1]
528bf80f4bSopenharmony_ci    cp1.x = BASE_NS::Math::clamp01(cp1.x);
538bf80f4bSopenharmony_ci    cp2.x = BASE_NS::Math::clamp01(cp2.x);
548bf80f4bSopenharmony_ci
558bf80f4bSopenharmony_ci    // Cache the polynomial coefficients, first and last control points are implicity [0,0] and [1,1]
568bf80f4bSopenharmony_ci    coeff_[2] = cp1 * 3.f;
578bf80f4bSopenharmony_ci    coeff_[1] = (cp2 - cp1) * 3.f - coeff_[2];
588bf80f4bSopenharmony_ci    coeff_[0] = BASE_NS::Math::Vec2(1.f, 1.f) - coeff_[2] - coeff_[1];
598bf80f4bSopenharmony_ci}
608bf80f4bSopenharmony_ci
618bf80f4bSopenharmony_ciconstexpr bool IsFloatNull(float v, float epsilon) noexcept
628bf80f4bSopenharmony_ci{
638bf80f4bSopenharmony_ci    return BASE_NS::Math::abs(v) < epsilon;
648bf80f4bSopenharmony_ci}
658bf80f4bSopenharmony_ci
668bf80f4bSopenharmony_cifloat CubicBezierEasingCurve::GetLinearX(float t) const noexcept
678bf80f4bSopenharmony_ci{
688bf80f4bSopenharmony_ci    static constexpr int newtonMaxIter = 8;
698bf80f4bSopenharmony_ci    static constexpr float epsilon = 0.001f;
708bf80f4bSopenharmony_ci
718bf80f4bSopenharmony_ci    if (t < 0) {
728bf80f4bSopenharmony_ci        return 0.f;
738bf80f4bSopenharmony_ci    }
748bf80f4bSopenharmony_ci    if (t > 1.f) {
758bf80f4bSopenharmony_ci        return 1.f;
768bf80f4bSopenharmony_ci    }
778bf80f4bSopenharmony_ci
788bf80f4bSopenharmony_ci    float x;
798bf80f4bSopenharmony_ci    float dx;
808bf80f4bSopenharmony_ci    float tt = t;
818bf80f4bSopenharmony_ci
828bf80f4bSopenharmony_ci    // Fast iteration with Newton's method
838bf80f4bSopenharmony_ci    for (int i = 0; i < newtonMaxIter; i++) {
848bf80f4bSopenharmony_ci        if (x = GetX(tt) - t; IsFloatNull(x, epsilon)) {
858bf80f4bSopenharmony_ci            return tt;
868bf80f4bSopenharmony_ci        }
878bf80f4bSopenharmony_ci        if (dx = GetDX(tt); IsFloatNull(dx, BASE_NS::Math::EPSILON)) {
888bf80f4bSopenharmony_ci            break;
898bf80f4bSopenharmony_ci        }
908bf80f4bSopenharmony_ci        tt = tt - x / dx;
918bf80f4bSopenharmony_ci    }
928bf80f4bSopenharmony_ci
938bf80f4bSopenharmony_ci    // Didn't get a result with fast iteration, fallback to linear bisection
948bf80f4bSopenharmony_ci    float t0 = 0.f;
958bf80f4bSopenharmony_ci    float t1 = 1.f;
968bf80f4bSopenharmony_ci
978bf80f4bSopenharmony_ci    while (t0 < t1) {
988bf80f4bSopenharmony_ci        if (x = GetX(tt); IsFloatNull(x - t, epsilon)) {
998bf80f4bSopenharmony_ci            return tt;
1008bf80f4bSopenharmony_ci        }
1018bf80f4bSopenharmony_ci        if (t > x) {
1028bf80f4bSopenharmony_ci            t0 = tt;
1038bf80f4bSopenharmony_ci        } else {
1048bf80f4bSopenharmony_ci            t1 = tt;
1058bf80f4bSopenharmony_ci        }
1068bf80f4bSopenharmony_ci        tt = (t1 - t0) * .5f + t0;
1078bf80f4bSopenharmony_ci    }
1088bf80f4bSopenharmony_ci
1098bf80f4bSopenharmony_ci    // Fallback
1108bf80f4bSopenharmony_ci    return tt;
1118bf80f4bSopenharmony_ci}
1128bf80f4bSopenharmony_ci
1138bf80f4bSopenharmony_ci} // namespace Easing
1148bf80f4bSopenharmony_ci} // namespace Curves
1158bf80f4bSopenharmony_ci
1168bf80f4bSopenharmony_ciMETA_END_NAMESPACE()
117