pebble/src/fw/applib/ui/animation_interpolate.c
2025-01-27 11:38:16 -08:00

179 lines
7.2 KiB
C

/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "animation.h"
#include "animation_interpolate.h"
#include "animation_private.h"
#include "applib/graphics/gtypes.h"
#include "system/passert.h"
#include "util/math.h"
#include "util/size.h"
int64_t interpolate_int64_linear(int32_t normalized, int64_t from, int64_t to) {
return from + ((normalized * (to - from)) / ANIMATION_NORMALIZED_MAX);
}
int64_t interpolate_int64(int32_t normalized, int64_t from, int64_t to) {
InterpolateInt64Function interpolate =
animation_private_current_interpolate_override() ?: interpolate_int64_linear;
return interpolate(normalized, from, to);
}
int16_t interpolate_int16(int32_t normalized, int16_t from, int16_t to) {
const int64_t interpolated = interpolate_int64(normalized, from, to);
return (int16_t)CLIP(interpolated, INT16_MIN, INT16_MAX);
}
uint32_t interpolate_uint32(int32_t normalized, uint32_t from, uint32_t to) {
const int64_t interpolated = interpolate_int64(normalized, from, to);
return (uint32_t)CLIP(interpolated, 0, UINT32_MAX);
}
Fixed_S32_16 interpolate_fixed32(int32_t normalized, Fixed_S32_16 from, Fixed_S32_16 to) {
const int64_t interpolated = interpolate_int64(normalized, from.raw_value, to.raw_value);
const int32_t raw_value =
(int32_t) CLIP(interpolated, INT32_MIN, INT32_MAX);
return Fixed_S32_16(raw_value);
}
GSize interpolate_gsize(int32_t normalized, GSize from, GSize to) {
return (GSize) {
.w = interpolate_int16(normalized, from.w, to.w),
.h = interpolate_int16(normalized, from.w, to.w),
};
}
GPoint interpolate_gpoint(int32_t normalized, GPoint from, GPoint to) {
return (GPoint) {
.x = interpolate_int16(normalized, from.x, to.x),
.y = interpolate_int16(normalized, from.y, to.y),
};
}
int16_t scale_int16(int16_t value, int16_t from, int16_t to) {
return (int16_t) ((int32_t) value * to / from);
}
int32_t scale_int32(int32_t value, int32_t from, int32_t to) {
return (int32_t) ((int64_t) value * to / from);
}
// -------------------------------------------------------
// these values are directly taken from "easing red line 001.mov"
// _in will be added to first value (easing in, anticipation)
// _out will be added to second value (overshoot, swing-back)
static const int32_t s_delta_moook_in[] = {0, 1, 20};
static const int32_t s_delta_moook_out[] = {INTERPOLATE_MOOOK_BOUNCE_BACK, 2, 1, 0};
// TODO: export these as interpolation functions as well
uint32_t interpolate_moook_in_duration() {
return ARRAY_LENGTH(s_delta_moook_in) * ANIMATION_TARGET_FRAME_INTERVAL_MS;
}
uint32_t interpolate_moook_out_duration() {
return ARRAY_LENGTH(s_delta_moook_out) * ANIMATION_TARGET_FRAME_INTERVAL_MS;
}
uint32_t interpolate_moook_duration() {
return interpolate_moook_in_duration() + interpolate_moook_out_duration();
}
uint32_t interpolate_moook_soft_duration(int32_t num_frames_mid) {
return interpolate_moook_duration() + num_frames_mid * ANIMATION_TARGET_FRAME_INTERVAL_MS;
}
uint32_t interpolate_moook_custom_duration(const MoookConfig *config) {
PBL_ASSERTN(config);
return ((config->num_frames_in + config->num_frames_mid + config->num_frames_out) *
ANIMATION_TARGET_FRAME_INTERVAL_MS);
}
static int64_t prv_interpolate_moook(
int32_t normalized, int64_t from, int64_t to, const int32_t *frames_in, int32_t num_frames_in,
const int32_t *frames_out, int32_t num_frames_out, int32_t num_frames_mid, bool bounce_back) {
const int32_t direction = ((from == to) ? 0 : ((from < to) ? 1 : -1));
if (direction == 0) {
return from;
}
const int32_t direction_out = direction * (bounce_back ? 1 : -1);
const size_t num_frames_total = num_frames_in + num_frames_mid + num_frames_out;
int32_t frame_idx =
((normalized * num_frames_total + (ANIMATION_NORMALIZED_MAX / (2 * num_frames_total))) /
ANIMATION_NORMALIZED_MAX);
frame_idx = CLIP(frame_idx, 0, (int)num_frames_total - 1);
if (normalized == ANIMATION_NORMALIZED_MAX) {
return to;
} else if (frame_idx < 0) {
return from;
} else if (frame_idx < num_frames_in) {
return from + (frames_in ? (direction * frames_in[frame_idx]) : 0);
} else if ((frame_idx < (num_frames_in + num_frames_mid)) && (num_frames_mid > 0)) {
const int64_t shifted_normalized = normalized -
(((int64_t) num_frames_in * ANIMATION_NORMALIZED_MAX) / num_frames_total);
const int32_t mid_normalized = ((int64_t) num_frames_total * shifted_normalized) /
num_frames_mid;
return interpolate_int64_linear(mid_normalized,
from + (direction * frames_in[num_frames_in - 1]),
to + (direction_out * frames_out[0]));
} else {
return to + (frames_out ? (direction_out *
frames_out[frame_idx - (num_frames_in + num_frames_mid)]) : 0);
}
}
int64_t interpolate_moook_in(int32_t normalized, int64_t from, int64_t to, int32_t num_frames_to) {
return prv_interpolate_moook(normalized, from, to, s_delta_moook_in,
ARRAY_LENGTH(s_delta_moook_in), NULL, num_frames_to, 0, true);
}
int64_t interpolate_moook_in_only(int32_t normalized, int64_t from, int64_t to) {
return prv_interpolate_moook(normalized, from, to, s_delta_moook_in,
ARRAY_LENGTH(s_delta_moook_in), NULL, 0, 0, true);
}
int64_t interpolate_moook_out(int32_t normalized, int64_t from, int64_t to,
int32_t num_frames_from, bool bounce_back) {
return prv_interpolate_moook(normalized, from, to, NULL, num_frames_from, s_delta_moook_out,
ARRAY_LENGTH(s_delta_moook_out), 0, bounce_back);
}
int64_t interpolate_moook(int32_t normalized, int64_t from, int64_t to) {
return prv_interpolate_moook(normalized, from, to,
s_delta_moook_in, ARRAY_LENGTH(s_delta_moook_in),
s_delta_moook_out, ARRAY_LENGTH(s_delta_moook_out), 0, true);
}
int64_t interpolate_moook_soft(int32_t normalized, int64_t from, int64_t to,
int32_t num_frames_mid) {
return prv_interpolate_moook(normalized, from, to,
s_delta_moook_in, ARRAY_LENGTH(s_delta_moook_in),
s_delta_moook_out, ARRAY_LENGTH(s_delta_moook_out),
num_frames_mid, true);
}
int64_t interpolate_moook_custom(int32_t normalized, int64_t from, int64_t to,
const MoookConfig *config) {
PBL_ASSERTN(config);
return prv_interpolate_moook(normalized, from, to,
config->frames_in, config->num_frames_in,
config->frames_out, config->num_frames_out,
config->num_frames_mid, !config->no_bounce_back);
}