Refactor bezier interpolation functions

This commit is contained in:
Hendrik Brucker 2022-06-27 19:41:32 +02:00
parent c41e4b10c3
commit 99ce0df3b1
14 changed files with 129 additions and 78 deletions

View File

@ -253,6 +253,27 @@ public:
(-p_pre + 3.0f * p_from - 3.0f * p_to + p_post) * (p_weight * p_weight * p_weight));
}
static _ALWAYS_INLINE_ double bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) {
/* Formula from Wikipedia article on Bezier curves. */
double omt = (1.0 - p_t);
double omt2 = omt * omt;
double omt3 = omt2 * omt;
double t2 = p_t * p_t;
double t3 = t2 * p_t;
return p_start * omt3 + p_control_1 * omt2 * p_t * 3.0 + p_control_2 * omt * t2 * 3.0 + p_end * t3;
}
static _ALWAYS_INLINE_ float bezier_interpolate(float p_start, float p_control_1, float p_control_2, float p_end, float p_t) {
/* Formula from Wikipedia article on Bezier curves. */
float omt = (1.0f - p_t);
float omt2 = omt * omt;
float omt3 = omt2 * omt;
float t2 = p_t * p_t;
float t3 = t2 * p_t;
return p_start * omt3 + p_control_1 * omt2 * p_t * 3.0f + p_control_2 * omt * t2 * 3.0f + p_end * t3;
}
static _ALWAYS_INLINE_ double lerp_angle(double p_from, double p_to, double p_weight) {
double difference = fmod(p_to - p_from, Math_TAU);
double distance = fmod(2.0 * difference, Math_TAU) - difference;

View File

@ -152,13 +152,6 @@ Vector2 Vector2::limit_length(const real_t p_len) const {
return v;
}
Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, const real_t p_weight) const {
Vector2 res = *this;
res.x = Math::cubic_interpolate(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight);
res.y = Math::cubic_interpolate(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight);
return res;
}
Vector2 Vector2::move_toward(const Vector2 &p_to, const real_t p_delta) const {
Vector2 v = *this;
Vector2 vd = p_to - v;

View File

@ -113,7 +113,9 @@ struct _NO_DISCARD_ Vector2 {
_FORCE_INLINE_ Vector2 lerp(const Vector2 &p_to, const real_t p_weight) const;
_FORCE_INLINE_ Vector2 slerp(const Vector2 &p_to, const real_t p_weight) const;
Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, const real_t p_weight) const;
_FORCE_INLINE_ Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, const real_t p_weight) const;
_FORCE_INLINE_ Vector2 bezier_interpolate(const Vector2 &p_control_1, const Vector2 &p_control_2, const Vector2 &p_end, const real_t p_t) const;
Vector2 move_toward(const Vector2 &p_to, const real_t p_delta) const;
Vector2 slide(const Vector2 &p_normal) const;
@ -261,6 +263,26 @@ Vector2 Vector2::slerp(const Vector2 &p_to, const real_t p_weight) const {
return rotated(angle * p_weight) * (result_length / start_length);
}
Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, const real_t p_weight) const {
Vector2 res = *this;
res.x = Math::cubic_interpolate(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight);
res.y = Math::cubic_interpolate(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight);
return res;
}
Vector2 Vector2::bezier_interpolate(const Vector2 &p_control_1, const Vector2 &p_control_2, const Vector2 &p_end, const real_t p_t) const {
Vector2 res = *this;
/* Formula from Wikipedia article on Bezier curves. */
real_t omt = (1.0 - p_t);
real_t omt2 = omt * omt;
real_t omt3 = omt2 * omt;
real_t t2 = p_t * p_t;
real_t t3 = t2 * p_t;
return res * omt3 + p_control_1 * omt2 * p_t * 3.0 + p_control_2 * omt * t2 * 3.0 + p_end * t3;
}
Vector2 Vector2::direction_to(const Vector2 &p_to) const {
Vector2 ret(p_to.x - x, p_to.y - y);
ret.normalize();

View File

@ -85,14 +85,6 @@ Vector3 Vector3::limit_length(const real_t p_len) const {
return v;
}
Vector3 Vector3::cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight) const {
Vector3 res = *this;
res.x = Math::cubic_interpolate(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight);
res.y = Math::cubic_interpolate(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight);
res.z = Math::cubic_interpolate(res.z, p_b.z, p_pre_a.z, p_post_b.z, p_weight);
return res;
}
Vector3 Vector3::move_toward(const Vector3 &p_to, const real_t p_delta) const {
Vector3 v = *this;
Vector3 vd = p_to - v;

View File

@ -104,7 +104,9 @@ struct _NO_DISCARD_ Vector3 {
_FORCE_INLINE_ Vector3 lerp(const Vector3 &p_to, const real_t p_weight) const;
_FORCE_INLINE_ Vector3 slerp(const Vector3 &p_to, const real_t p_weight) const;
Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight) const;
_FORCE_INLINE_ Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight) const;
_FORCE_INLINE_ Vector3 bezier_interpolate(const Vector3 &p_control_1, const Vector3 &p_control_2, const Vector3 &p_end, const real_t p_t) const;
Vector3 move_toward(const Vector3 &p_to, const real_t p_delta) const;
Vector2 octahedron_encode() const;
@ -227,6 +229,27 @@ Vector3 Vector3::slerp(const Vector3 &p_to, const real_t p_weight) const {
return rotated(cross(p_to).normalized(), angle * p_weight) * (result_length / start_length);
}
Vector3 Vector3::cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight) const {
Vector3 res = *this;
res.x = Math::cubic_interpolate(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight);
res.y = Math::cubic_interpolate(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight);
res.z = Math::cubic_interpolate(res.z, p_b.z, p_pre_a.z, p_post_b.z, p_weight);
return res;
}
Vector3 Vector3::bezier_interpolate(const Vector3 &p_control_1, const Vector3 &p_control_2, const Vector3 &p_end, const real_t p_t) const {
Vector3 res = *this;
/* Formula from Wikipedia article on Bezier curves. */
real_t omt = (1.0 - p_t);
real_t omt2 = omt * omt;
real_t omt3 = omt2 * omt;
real_t t2 = p_t * p_t;
real_t t3 = t2 * p_t;
return res * omt3 + p_control_1 * omt2 * p_t * 3.0 + p_control_2 * omt * t2 * 3.0 + p_end * t3;
}
real_t Vector3::distance_to(const Vector3 &p_to) const {
return (p_to - *this).length();
}

View File

@ -1556,6 +1556,7 @@ static void _register_variant_builtin_methods() {
bind_method(Vector2, lerp, sarray("to", "weight"), varray());
bind_method(Vector2, slerp, sarray("to", "weight"), varray());
bind_method(Vector2, cubic_interpolate, sarray("b", "pre_a", "post_b", "weight"), varray());
bind_method(Vector2, bezier_interpolate, sarray("control_1", "control_2", "end", "t"), varray());
bind_method(Vector2, max_axis_index, sarray(), varray());
bind_method(Vector2, min_axis_index, sarray(), varray());
bind_method(Vector2, move_toward, sarray("to", "delta"), varray());
@ -1643,6 +1644,7 @@ static void _register_variant_builtin_methods() {
bind_method(Vector3, lerp, sarray("to", "weight"), varray());
bind_method(Vector3, slerp, sarray("to", "weight"), varray());
bind_method(Vector3, cubic_interpolate, sarray("b", "pre_a", "post_b", "weight"), varray());
bind_method(Vector3, bezier_interpolate, sarray("control_1", "control_2", "end", "t"), varray());
bind_method(Vector3, move_toward, sarray("to", "delta"), varray());
bind_method(Vector3, dot, sarray("with"), varray());
bind_method(Vector3, cross, sarray("with"), varray());

View File

@ -231,6 +231,10 @@ struct VariantUtilityFunctions {
return Math::cubic_interpolate(from, to, pre, post, weight);
}
static inline double bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) {
return Math::bezier_interpolate(p_start, p_control_1, p_control_2, p_end, p_t);
}
static inline double lerp_angle(double from, double to, double weight) {
return Math::lerp_angle(from, to, weight);
}
@ -1204,6 +1208,7 @@ void Variant::_register_variant_utility_functions() {
FUNCBINDR(lerp, sarray("from", "to", "weight"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(cubic_interpolate, sarray("from", "to", "pre", "post", "weight"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(bezier_interpolate, sarray("start", "control_1", "control_2", "end", "t"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(lerp_angle, sarray("from", "to", "weight"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(inverse_lerp, sarray("from", "to", "weight"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(range_lerp, sarray("value", "istart", "istop", "ostart", "ostop"), Variant::UTILITY_FUNC_TYPE_MATH);

View File

@ -106,6 +106,17 @@
[/codeblock]
</description>
</method>
<method name="bezier_interpolate">
<return type="float" />
<argument index="0" name="start" type="float" />
<argument index="1" name="control_1" type="float" />
<argument index="2" name="control_2" type="float" />
<argument index="3" name="end" type="float" />
<argument index="4" name="t" type="float" />
<description>
Returns the point at the given [code]t[/code] on a one-dimnesional [url=https://en.wikipedia.org/wiki/B%C3%A9zier_curve]Bezier curve[/url] defined by the given [code]control_1[/code], [code]control_2[/code], and [code]end[/code] points.
</description>
</method>
<method name="bytes2var">
<return type="Variant" />
<argument index="0" name="bytes" type="PackedByteArray" />

View File

@ -85,6 +85,16 @@
Returns the aspect ratio of this vector, the ratio of [member x] to [member y].
</description>
</method>
<method name="bezier_interpolate" qualifiers="const">
<return type="Vector2" />
<argument index="0" name="control_1" type="Vector2" />
<argument index="1" name="control_2" type="Vector2" />
<argument index="2" name="end" type="Vector2" />
<argument index="3" name="t" type="float" />
<description>
Returns the point at the given [code]t[/code] on the [url=https://en.wikipedia.org/wiki/B%C3%A9zier_curve]Bezier curve[/url] defined by this vector and the given [code]control_1[/code], [code]control_2[/code], and [code]end[/code] points.
</description>
</method>
<method name="bounce" qualifiers="const">
<return type="Vector2" />
<argument index="0" name="n" type="Vector2" />

View File

@ -61,6 +61,16 @@
Returns the unsigned minimum angle to the given vector, in radians.
</description>
</method>
<method name="bezier_interpolate" qualifiers="const">
<return type="Vector3" />
<argument index="0" name="control_1" type="Vector3" />
<argument index="1" name="control_2" type="Vector3" />
<argument index="2" name="end" type="Vector3" />
<argument index="3" name="t" type="float" />
<description>
Returns the point at the given [code]t[/code] on the [url=https://en.wikipedia.org/wiki/B%C3%A9zier_curve]Bezier curve[/url] defined by this vector and the given [code]control_1[/code], [code]control_2[/code], and [code]end[/code] points.
</description>
</method>
<method name="bounce" qualifiers="const">
<return type="Vector3" />
<argument index="0" name="n" type="Vector3" />

View File

@ -44,17 +44,6 @@ float AnimationBezierTrackEdit::_bezier_h_to_pixel(float p_h) {
return h;
}
static _FORCE_INLINE_ Vector2 _bezier_interp(real_t t, const Vector2 &start, const Vector2 &control_1, const Vector2 &control_2, const Vector2 &end) {
/* Formula from Wikipedia article on Bezier curves. */
real_t omt = (1.0 - t);
real_t omt2 = omt * omt;
real_t omt3 = omt2 * omt;
real_t t2 = t * t;
real_t t3 = t2 * t;
return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3;
}
void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
float scale = timeline->get_zoom_scale();
@ -151,7 +140,7 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
for (int k = 0; k < iterations; k++) {
float middle = (low + high) / 2;
Vector2 interp = _bezier_interp(middle, start, out_handle, in_handle, end);
Vector2 interp = start.bezier_interpolate(out_handle, in_handle, end, middle);
if (interp.x < t) {
low = middle;
@ -161,8 +150,8 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
}
//interpolate the result:
Vector2 low_pos = _bezier_interp(low, start, out_handle, in_handle, end);
Vector2 high_pos = _bezier_interp(high, start, out_handle, in_handle, end);
Vector2 low_pos = start.bezier_interpolate(out_handle, in_handle, end, low);
Vector2 high_pos = start.bezier_interpolate(out_handle, in_handle, end, high);
float c = (t - low_pos.x) / (high_pos.x - low_pos.x);

View File

@ -3379,17 +3379,6 @@ Vector2 Animation::bezier_track_get_key_out_handle(int p_track, int p_index) con
return bt->values[p_index].value.out_handle;
}
static _FORCE_INLINE_ Vector2 _bezier_interp(real_t t, const Vector2 &start, const Vector2 &control_1, const Vector2 &control_2, const Vector2 &end) {
/* Formula from Wikipedia article on Bezier curves. */
real_t omt = (1.0 - t);
real_t omt2 = omt * omt;
real_t omt3 = omt2 * omt;
real_t t2 = t * t;
real_t t3 = t2 * t;
return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3;
}
real_t Animation::bezier_track_interpolate(int p_track, double p_time) const {
//this uses a different interpolation scheme
ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
@ -3438,7 +3427,7 @@ real_t Animation::bezier_track_interpolate(int p_track, double p_time) const {
for (int i = 0; i < iterations; i++) {
real_t middle = (low + high) / 2;
Vector2 interp = _bezier_interp(middle, start, start_out, end_in, end);
Vector2 interp = start.bezier_interpolate(start_out, end_in, end, middle);
if (interp.x < t) {
low = middle;
@ -3448,8 +3437,8 @@ real_t Animation::bezier_track_interpolate(int p_track, double p_time) const {
}
//interpolate the result:
Vector2 low_pos = _bezier_interp(low, start, start_out, end_in, end);
Vector2 high_pos = _bezier_interp(high, start, start_out, end_in, end);
Vector2 low_pos = start.bezier_interpolate(start_out, end_in, end, low);
Vector2 high_pos = start.bezier_interpolate(start_out, end_in, end, high);
real_t c = (t - low_pos.x) / (high_pos.x - low_pos.x);
return low_pos.lerp(high_pos, c).y;

View File

@ -32,18 +32,6 @@
#include "core/core_string_names.h"
template <class T>
static _FORCE_INLINE_ T _bezier_interp(real_t p_t, T p_start, T p_control_1, T p_control_2, T p_end) {
/* Formula from Wikipedia article on Bezier curves. */
real_t omt = (1.0 - p_t);
real_t omt2 = omt * omt;
real_t omt3 = omt2 * omt;
real_t t2 = p_t * p_t;
real_t t3 = t2 * p_t;
return p_start * omt3 + p_control_1 * omt2 * p_t * 3.0 + p_control_2 * omt * t2 * 3.0 + p_end * t3;
}
const char *Curve::SIGNAL_RANGE_CHANGED = "range_changed";
Curve::Curve() {
@ -376,7 +364,7 @@ real_t Curve::interpolate_local_nocheck(int p_index, real_t p_local_offset) cons
real_t yac = a.position.y + d * a.right_tangent;
real_t ybc = b.position.y - d * b.left_tangent;
real_t y = _bezier_interp(p_local_offset, a.position.y, yac, ybc, b.position.y);
real_t y = Math::bezier_interpolate(a.position.y, yac, ybc, b.position.y, p_local_offset);
return y;
}
@ -747,7 +735,7 @@ Vector2 Curve2D::interpolate(int p_index, const real_t p_offset) const {
Vector2 p3 = points[p_index + 1].position;
Vector2 p2 = p3 + points[p_index + 1].in;
return _bezier_interp(p_offset, p0, p1, p2, p3);
return p0.bezier_interpolate(p1, p2, p3, p_offset);
}
Vector2 Curve2D::interpolatef(real_t p_findex) const {
@ -767,9 +755,9 @@ void Curve2D::mark_dirty() {
void Curve2D::_bake_segment2d(RBMap<real_t, Vector2> &r_bake, real_t p_begin, real_t p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_max_depth, real_t p_tol) const {
real_t mp = p_begin + (p_end - p_begin) * 0.5;
Vector2 beg = _bezier_interp(p_begin, p_a, p_a + p_out, p_b + p_in, p_b);
Vector2 mid = _bezier_interp(mp, p_a, p_a + p_out, p_b + p_in, p_b);
Vector2 end = _bezier_interp(p_end, p_a, p_a + p_out, p_b + p_in, p_b);
Vector2 beg = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, p_begin);
Vector2 mid = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, mp);
Vector2 end = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, p_end);
Vector2 na = (mid - beg).normalized();
Vector2 nb = (end - mid).normalized();
@ -828,7 +816,7 @@ void Curve2D::_bake() const {
np = 1.0;
}
Vector2 npp = _bezier_interp(np, points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position);
Vector2 npp = points[i].position.bezier_interpolate(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, np);
real_t d = position.distance_to(npp);
if (d > bake_interval) {
@ -841,7 +829,7 @@ void Curve2D::_bake() const {
real_t mid = low + (hi - low) * 0.5;
for (int j = 0; j < iterations; j++) {
npp = _bezier_interp(mid, points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position);
npp = points[i].position.bezier_interpolate(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, mid);
d = position.distance_to(npp);
if (bake_interval < d) {
@ -1336,7 +1324,7 @@ Vector3 Curve3D::interpolate(int p_index, real_t p_offset) const {
Vector3 p3 = points[p_index + 1].position;
Vector3 p2 = p3 + points[p_index + 1].in;
return _bezier_interp(p_offset, p0, p1, p2, p3);
return p0.bezier_interpolate(p1, p2, p3, p_offset);
}
Vector3 Curve3D::interpolatef(real_t p_findex) const {
@ -1356,9 +1344,9 @@ void Curve3D::mark_dirty() {
void Curve3D::_bake_segment3d(RBMap<real_t, Vector3> &r_bake, real_t p_begin, real_t p_end, const Vector3 &p_a, const Vector3 &p_out, const Vector3 &p_b, const Vector3 &p_in, int p_depth, int p_max_depth, real_t p_tol) const {
real_t mp = p_begin + (p_end - p_begin) * 0.5;
Vector3 beg = _bezier_interp(p_begin, p_a, p_a + p_out, p_b + p_in, p_b);
Vector3 mid = _bezier_interp(mp, p_a, p_a + p_out, p_b + p_in, p_b);
Vector3 end = _bezier_interp(p_end, p_a, p_a + p_out, p_b + p_in, p_b);
Vector3 beg = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, p_begin);
Vector3 mid = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, mp);
Vector3 end = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, p_end);
Vector3 na = (mid - beg).normalized();
Vector3 nb = (end - mid).normalized();
@ -1426,7 +1414,7 @@ void Curve3D::_bake() const {
np = 1.0;
}
Vector3 npp = _bezier_interp(np, points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position);
Vector3 npp = points[i].position.bezier_interpolate(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, np);
real_t d = position.distance_to(npp);
if (d > bake_interval) {
@ -1439,7 +1427,7 @@ void Curve3D::_bake() const {
real_t mid = low + (hi - low) * 0.5;
for (int j = 0; j < iterations; j++) {
npp = _bezier_interp(mid, points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position);
npp = points[i].position.bezier_interpolate(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, mid);
d = position.distance_to(npp);
if (bake_interval < d) {

View File

@ -92,10 +92,6 @@ public:
void set_interpolation_mode(InterpolationMode p_interp_mode);
InterpolationMode get_interpolation_mode();
_FORCE_INLINE_ float cubic_interpolate(float p0, float p1, float p2, float p3, float x) {
return p1 + 0.5 * x * (p2 - p0 + x * (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3 + x * (3.0 * (p1 - p2) + p3 - p0)));
}
_FORCE_INLINE_ Color get_color_at_offset(float p_offset) {
if (points.is_empty()) {
return Color(0, 0, 0, 1);
@ -161,10 +157,10 @@ public:
const Point &pointP3 = points[p3];
float x = (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset);
float r = cubic_interpolate(pointP0.color.r, pointFirst.color.r, pointSecond.color.r, pointP3.color.r, x);
float g = cubic_interpolate(pointP0.color.g, pointFirst.color.g, pointSecond.color.g, pointP3.color.g, x);
float b = cubic_interpolate(pointP0.color.b, pointFirst.color.b, pointSecond.color.b, pointP3.color.b, x);
float a = cubic_interpolate(pointP0.color.a, pointFirst.color.a, pointSecond.color.a, pointP3.color.a, x);
float r = Math::cubic_interpolate(pointP0.color.r, pointFirst.color.r, pointSecond.color.r, pointP3.color.r, x);
float g = Math::cubic_interpolate(pointP0.color.g, pointFirst.color.g, pointSecond.color.g, pointP3.color.g, x);
float b = Math::cubic_interpolate(pointP0.color.b, pointFirst.color.b, pointSecond.color.b, pointP3.color.b, x);
float a = Math::cubic_interpolate(pointP0.color.a, pointFirst.color.a, pointSecond.color.a, pointP3.color.a, x);
return Color(r, g, b, a);
} break;