Fix 'Held' interpolation of timesamples value.

This commit is contained in:
Syoyo Fujita
2024-05-08 03:01:40 +09:00
parent b5b1991141
commit 7615104c7e
8 changed files with 187 additions and 38 deletions

View File

@@ -1168,16 +1168,24 @@ struct TypedTimeSamples {
return true;
}
auto it = std::lower_bound(
// Held = nerarest preceding value for a gien time.
// example:
// input = 0.0: 100, 1.0: 200
//
// t -1.0 => 100(time 0.0)
// t 0.0 => 100(time 0.0)
// t 0.1 => 100(time 0.0)
// t 0.9 => 100(time 0.0)
// t 1.0 => 200(time 1.0)
//
// This can be achieved by using upper_bound, and subtract 1 from the found position.
auto it = std::upper_bound(
_samples.begin(), _samples.end(), t,
[](const Sample &a, double tval) { return a.t < tval; });
[](double tval, const Sample &a) { return tval < a.t; });
if (it == _samples.end()) {
// ???
return false;
}
const auto it_minus_1 = (it == _samples.begin()) ? _samples.begin() : (it - 1);
(*dst) = it->value;
(*dst) = it_minus_1->value;
return true;
}

View File

@@ -50,12 +50,24 @@ bool PrimVar::get_interpolated_value(const double t, const value::TimeSampleInte
(*dst) = samples[0].value;
return true;
} else {
// TODO: Unify code in prim-types.hh
auto it = std::lower_bound(
samples.begin(), samples.end(), t,
[](const value::TimeSamples::Sample &a, double tval) { return a.t < tval; });
if (tinterp == value::TimeSampleInterpolationType::Linear) {
if (tinterp == value::TimeSampleInterpolationType::Held || !value::IsLerpSupportedType(_value.type_id())) {
auto it = std::upper_bound(
samples.begin(), samples.end(), t,
[](double tval, const value::TimeSamples::Sample &a) { return tval < a.t; });
const auto it_minus_1 = (it == samples.begin()) ? samples.begin() : (it - 1);
(*dst) = it_minus_1->value;
return true;
} else { // Lerp
// TODO: Unify code in prim-types.hh
auto it = std::lower_bound(
samples.begin(), samples.end(), t,
[](const value::TimeSamples::Sample &a, double tval) { return a.t < tval; });
const auto it_minus_1 = (it == samples.begin()) ? samples.begin() : (it - 1);
@@ -86,18 +98,15 @@ bool PrimVar::get_interpolated_value(const double t, const value::TimeSampleInte
bool ret = value::Lerp(p0, p1, dt, dst);
return ret;
} else {
if (it == samples.end()) {
// ???
return false;
}
(*dst) = it->value;
return true;
}
}
}
if (has_default()) {
(*dst) = _value;
return true;
}
return false;
}

View File

@@ -295,6 +295,7 @@ struct PrimVar {
if (auto pv = get_default_value<T>()) {
(*v) = pv.value();
return true;
}
if (_ts.empty()) {
@@ -302,7 +303,18 @@ struct PrimVar {
}
}
return _ts.get(v, t, tinterp);
if (has_timesamples()) {
return _ts.get(v, t, tinterp);
}
if (has_default()) {
if (auto pv = get_default_value<T>()) {
(*v) = pv.value();
return true;
}
}
return false;
}
size_t num_timesamples() const {

View File

@@ -2412,16 +2412,13 @@ struct TimeSamples {
return false;
}
auto it = std::lower_bound(
auto it = std::upper_bound(
_samples.begin(), _samples.end(), t,
[](const Sample &a, double tval) { return a.t < tval; });
[](double tval, const Sample &a) { return tval < a.t; });
if (it == _samples.end()) {
// ???
return false;
}
const auto it_minus_1 = (it == _samples.begin()) ? _samples.begin() : (it - 1);
const value::Value &v = it->value;
const value::Value &v = it_minus_1->value;
if (const T *pv = v.as<T>()) {
(*dst) = *pv;
@@ -2468,11 +2465,11 @@ struct TimeSamples {
return true;
}
auto it = std::lower_bound(
_samples.begin(), _samples.end(), t,
[](const Sample &a, double tval) { return a.t < tval; });
if (interp == TimeSampleInterpolationType::Linear) {
auto it = std::lower_bound(
_samples.begin(), _samples.end(), t,
[](const Sample &a, double tval) { return a.t < tval; });
// MS STL does not allow seek vector iterator before begin
// Issue #110
@@ -2514,17 +2511,20 @@ struct TimeSamples {
}
return false;
} else {
if (it == _samples.end()) {
// ???
return false;
}
// Held
auto it = std::upper_bound(
_samples.begin(), _samples.end(), t,
[](double tval, const Sample &a) { return tval < a.t; });
const auto it_minus_1 = (it == _samples.begin()) ? _samples.begin() : (it - 1);
const value::Value &v = it_minus_1->value;
const value::Value &v = it->value;
if (const T *pv = v.as<T>()) {
(*dst) = *pv;
return true;
}
return false;
}
}

View File

@@ -13,6 +13,7 @@ set(TEST_SOURCES
unit-xform.cc
unit-math.cc
unit-ioutil.cc
unit-timesamples.cc
)
if (TINYUSDZ_WITH_PXR_COMPAT_API)

View File

@@ -14,6 +14,7 @@
#include "unit-math.h"
#include "unit-ioutil.h"
#include "unit-strutil.h"
#include "unit-timesamples.h"
#if defined(TINYUSDZ_WITH_PXR_COMPAT_API)
#include "unit-pxr-compat-api.h"
@@ -35,6 +36,7 @@ TEST_LIST = {
{ "pathutil_test", pathutil_test },
{ "ioutil_test", ioutil_test },
{ "strutil_test", strutil_test },
{ "timesamples_test", timesamples_test },
#if defined(TINYUSDZ_WITH_PXR_COMPAT_API)
{ "pxr_compat_api_test", pxr_compat_api_test },
#endif

View File

@@ -0,0 +1,114 @@
#ifdef _MSC_VER
#define NOMINMAX
#endif
#define TEST_NO_MAIN
#include "acutest.h"
#include "unit-timesamples.h"
#include "prim-types.hh"
#include "math-util.inc"
using namespace tinyusdz;
void timesamples_test(void) {
{
value::token tok1("bora");
value::token tok2("muda");
Animatable<value::token> toks;
toks.add_sample(0, tok1);
toks.add_sample(10, tok2);
{
value::token tok;
TEST_CHECK(toks.get(value::TimeCode::Default(), &tok));
// return the value of the first item(= timecode 0)
TEST_CHECK(tok.str() == "bora");
}
// Held interpolation
{
value::token tok;
TEST_CHECK(toks.get(0.0, &tok));
TEST_CHECK(tok.str() == "bora");
TEST_CHECK(toks.get(-1.0, &tok));
TEST_CHECK(tok.str() == "bora");
TEST_CHECK(toks.get(1.0, &tok));
TEST_CHECK(tok.str() == "bora");
TEST_CHECK(toks.get(10.0, &tok));
TEST_CHECK(tok.str() == "muda");
TEST_CHECK(toks.get(1000.0, &tok));
TEST_CHECK(tok.str() == "muda");
}
}
{
Animatable<float> samples;
samples.add_sample(0, 0.0f);
samples.add_sample(1, 10.0f);
{
float f;
TEST_CHECK(samples.get(value::TimeCode::Default(), &f));
// return the value of the first item(= timecode 0)
TEST_CHECK(math::is_close(f, 0.0f));
}
// Linear interpolation
{
float f;
TEST_CHECK(samples.get(0.0, &f));
TEST_CHECK(math::is_close(f, 0.0f));
TEST_CHECK(samples.get(0.5, &f));
TEST_CHECK(math::is_close(f, 5.0f));
TEST_CHECK(samples.get(1.0, &f));
TEST_CHECK(math::is_close(f, 10.0f));
}
}
{
primvar::PrimVar pbar;
value::TimeSamples ts;
ts.add_sample(0, value::Value(0.0f));
ts.add_sample(1, value::Value(10.0f));
pbar.set_timesamples(ts);
pbar.set_value(2000.0f); // default value
{
float f;
TEST_CHECK(pbar.get_interpolated_value(value::TimeCode::Default(), value::TimeSampleInterpolationType::Held, &f));
// return the value of the first item(= timecode 0)
TEST_CHECK(math::is_close(f, 2000.0f));
}
// Linear interpolation
{
float f;
TEST_CHECK(pbar.get_interpolated_value(-10.0, value::TimeSampleInterpolationType::Linear, &f));
TEST_CHECK(math::is_close(f, 0.0f));
TEST_CHECK(pbar.get_interpolated_value(0.0, value::TimeSampleInterpolationType::Linear, &f));
TEST_CHECK(math::is_close(f, 0.0f));
TEST_CHECK(pbar.get_interpolated_value(0.5, value::TimeSampleInterpolationType::Linear, &f));
TEST_CHECK(math::is_close(f, 5.0f));
TEST_CHECK(pbar.get_interpolated_value(1.0, value::TimeSampleInterpolationType::Linear, &f));
TEST_CHECK(math::is_close(f, 10.0f));
TEST_CHECK(pbar.get_interpolated_value(value::TimeCode::Default(), value::TimeSampleInterpolationType::Linear, &f));
TEST_CHECK(math::is_close(f, 2000.0f));
}
}
}

View File

@@ -0,0 +1,3 @@
#pragma once
void timesamples_test(void);