initial commit

This commit is contained in:
2026-04-07 20:55:30 +08:00
commit 81d1fb7856
84 changed files with 11929 additions and 0 deletions

View File

@@ -0,0 +1,191 @@
#include "animation/AnimationSampling.h"
#include <algorithm>
namespace core {
namespace {
template <typename KeyT, typename FrameGetter>
void sortKeysByFrame(QVector<KeyT>& keys, FrameGetter getFrame) {
std::sort(keys.begin(), keys.end(), [&](const KeyT& a, const KeyT& b) { return getFrame(a) < getFrame(b); });
}
} // namespace
QPointF sampleLocation(const QVector<Project::Entity::KeyframeVec2>& keys,
int frame,
const QPointF& fallbackOrigin,
KeyInterpolation mode) {
QVector<Project::Entity::KeyframeVec2> sorted = keys;
sortKeysByFrame(sorted, [](const Project::Entity::KeyframeVec2& k) { return k.frame; });
if (sorted.isEmpty()) {
return fallbackOrigin;
}
if (mode == KeyInterpolation::Hold) {
QPointF out = fallbackOrigin;
int best = -1;
for (const auto& k : sorted) {
if (k.frame <= frame && k.frame >= best) {
best = k.frame;
out = k.value;
}
}
return out;
}
// Linear区间外夹持到端点中间在相邻关键帧间线性插值对 x、y 分别 lerp
const auto& first = sorted.front();
const auto& last = sorted.back();
if (frame <= first.frame) {
return first.value;
}
if (frame >= last.frame) {
return last.value;
}
for (int i = 0; i + 1 < sorted.size(); ++i) {
const int f0 = sorted[i].frame;
const int f1 = sorted[i + 1].frame;
if (frame < f0) {
continue;
}
if (frame <= f1) {
if (f1 == f0 || frame == f0) {
return sorted[i].value;
}
const double t = static_cast<double>(frame - f0) / static_cast<double>(f1 - f0);
const QPointF& a = sorted[i].value;
const QPointF& b = sorted[i + 1].value;
return QPointF(a.x() + (b.x() - a.x()) * t, a.y() + (b.y() - a.y()) * t);
}
}
return last.value;
}
double sampleDepthScale01(const QVector<Project::Entity::KeyframeFloat01>& keys,
int frame,
double fallback01,
KeyInterpolation mode) {
QVector<Project::Entity::KeyframeFloat01> sorted = keys;
sortKeysByFrame(sorted, [](const Project::Entity::KeyframeFloat01& k) { return k.frame; });
const double fb = std::clamp(fallback01, 0.0, 1.0);
if (sorted.isEmpty()) {
return fb;
}
if (mode == KeyInterpolation::Hold) {
double out = fb;
int best = -1;
for (const auto& k : sorted) {
if (k.frame <= frame && k.frame >= best) {
best = k.frame;
out = k.value;
}
}
return std::clamp(out, 0.0, 1.0);
}
const auto& first = sorted.front();
const auto& last = sorted.back();
if (frame <= first.frame) {
return std::clamp(first.value, 0.0, 1.0);
}
if (frame >= last.frame) {
return std::clamp(last.value, 0.0, 1.0);
}
for (int i = 0; i + 1 < sorted.size(); ++i) {
const int f0 = sorted[i].frame;
const int f1 = sorted[i + 1].frame;
if (frame < f0) {
continue;
}
if (frame <= f1) {
if (f1 == f0 || frame == f0) {
return std::clamp(sorted[i].value, 0.0, 1.0);
}
const double t = static_cast<double>(frame - f0) / static_cast<double>(f1 - f0);
const double a = sorted[i].value;
const double b = sorted[i + 1].value;
return std::clamp(a + (b - a) * t, 0.0, 1.0);
}
}
return std::clamp(last.value, 0.0, 1.0);
}
double sampleUserScale(const QVector<Project::Entity::KeyframeDouble>& keys,
int frame,
double fallback,
KeyInterpolation mode) {
QVector<Project::Entity::KeyframeDouble> sorted = keys;
sortKeysByFrame(sorted, [](const Project::Entity::KeyframeDouble& k) { return k.frame; });
const double fb = std::max(fallback, 1e-6);
if (sorted.isEmpty()) {
return fb;
}
if (mode == KeyInterpolation::Hold) {
double out = fb;
int best = -1;
for (const auto& k : sorted) {
if (k.frame <= frame && k.frame >= best) {
best = k.frame;
out = k.value;
}
}
return std::max(out, 1e-6);
}
const auto& first = sorted.front();
const auto& last = sorted.back();
if (frame <= first.frame) {
return std::max(first.value, 1e-6);
}
if (frame >= last.frame) {
return std::max(last.value, 1e-6);
}
for (int i = 0; i + 1 < sorted.size(); ++i) {
const int f0 = sorted[i].frame;
const int f1 = sorted[i + 1].frame;
if (frame < f0) {
continue;
}
if (frame <= f1) {
if (f1 == f0 || frame == f0) {
return std::max(sorted[i].value, 1e-6);
}
const double t = static_cast<double>(frame - f0) / static_cast<double>(f1 - f0);
const double a = sorted[i].value;
const double b = sorted[i + 1].value;
return std::max(a + (b - a) * t, 1e-6);
}
}
return std::max(last.value, 1e-6);
}
QString sampleImagePath(const QVector<Project::Entity::ImageFrame>& frames,
int frame,
const QString& fallbackPath) {
QVector<Project::Entity::ImageFrame> sorted = frames;
sortKeysByFrame(sorted, [](const Project::Entity::ImageFrame& k) { return k.frame; });
QString out = fallbackPath;
int best = -1;
for (const auto& k : sorted) {
if (k.frame <= frame && k.frame >= best && !k.imagePath.isEmpty()) {
best = k.frame;
out = k.imagePath;
}
}
return out;
}
} // namespace core