AnimTestbed/3rdparty/ozz-animation/test/animation/runtime/track_triggering_job_tests.cc

1407 lines
44 KiB
C++

//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#include "gtest/gtest.h"
#include "ozz/animation/offline/raw_track.h"
#include "ozz/animation/offline/track_builder.h"
#include "ozz/animation/runtime/track.h"
#include "ozz/animation/runtime/track_triggering_job.h"
#include "ozz/base/gtest_helper.h"
#include "ozz/base/maths/gtest_math_helper.h"
#include "ozz/base/memory/unique_ptr.h"
using ozz::animation::FloatTrack;
using ozz::animation::TrackTriggeringJob;
using ozz::animation::offline::RawFloatTrack;
using ozz::animation::offline::RawTrackInterpolation;
using ozz::animation::offline::TrackBuilder;
TEST(JobValidity, TrackTriggeringJob) {
// Builds track
ozz::animation::offline::RawFloatTrack raw_track;
TrackBuilder builder;
ozz::unique_ptr<FloatTrack> track(builder(raw_track));
ASSERT_TRUE(track);
{ // Default is invalid
TrackTriggeringJob job;
EXPECT_FALSE(job.Validate());
EXPECT_FALSE(job.Run());
}
{ // No track
TrackTriggeringJob job;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
EXPECT_FALSE(job.Validate());
EXPECT_FALSE(job.Run());
}
{ // No output
TrackTriggeringJob job;
job.track = track.get();
EXPECT_FALSE(job.Validate());
EXPECT_FALSE(job.Run());
}
{ // Valid
TrackTriggeringJob job;
job.track = track.get();
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
EXPECT_TRUE(job.Validate());
EXPECT_TRUE(job.Run());
}
{ // Valid
TrackTriggeringJob job;
job.from = 0.f;
job.to = 1.f;
job.track = track.get();
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
EXPECT_TRUE(job.Validate());
EXPECT_TRUE(job.Run());
}
{ // Empty output is valid
TrackTriggeringJob job;
job.track = track.get();
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
EXPECT_TRUE(job.Validate());
EXPECT_TRUE(job.Run());
}
}
TEST(Default, TrackEdgeTriggerJob) {
FloatTrack default_track;
TrackTriggeringJob job;
job.track = &default_track;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
EXPECT_EQ(iterator, job.end());
}
TEST(Empty, TrackEdgeTriggerJob) {
TrackBuilder builder;
// Builds track
ozz::animation::offline::RawFloatTrack raw_track;
ozz::unique_ptr<FloatTrack> track(builder(raw_track));
ASSERT_TRUE(track);
TrackTriggeringJob job;
job.track = track.get();
job.threshold = 1.f;
job.from = 0.f;
job.to = 1.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
EXPECT_EQ(iterator, job.end());
}
TEST(Iterator, TrackEdgeTriggerJob) {
TrackBuilder builder;
ozz::animation::offline::RawFloatTrack raw_track;
// Keyframe values oscillate in range [0,2].
const ozz::animation::offline::RawFloatTrack::Keyframe key0 = {
RawTrackInterpolation::kStep, 0.f, 0.f};
raw_track.keyframes.push_back(key0);
const ozz::animation::offline::RawFloatTrack::Keyframe key1 = {
RawTrackInterpolation::kStep, .5f, 2.f};
raw_track.keyframes.push_back(key1);
const ozz::animation::offline::RawFloatTrack::Keyframe key2 = {
RawTrackInterpolation::kStep, 1.f, 0.f};
raw_track.keyframes.push_back(key2);
// Builds track
ozz::unique_ptr<FloatTrack> track(builder(raw_track));
ASSERT_TRUE(track);
TrackTriggeringJob job;
job.track = track.get();
job.threshold = 1.f;
job.from = 0.f;
job.to = 2.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
EXPECT_TRUE(iterator == iterator);
EXPECT_FALSE(iterator != iterator);
EXPECT_TRUE(job.end() == job.end());
EXPECT_FALSE(job.end() != job.end());
EXPECT_TRUE(iterator != job.end());
EXPECT_FALSE(iterator == job.end());
TrackTriggeringJob::Iterator iterator_cpy(iterator);
EXPECT_TRUE(iterator == iterator_cpy);
EXPECT_FALSE(iterator != iterator_cpy);
TrackTriggeringJob::Iterator default_iterator;
EXPECT_TRUE(default_iterator == default_iterator);
EXPECT_FALSE(default_iterator != default_iterator);
EXPECT_TRUE(default_iterator != iterator);
EXPECT_FALSE(default_iterator == iterator);
EXPECT_TRUE(default_iterator != job.end());
EXPECT_FALSE(default_iterator == job.end());
{ // Other jobs
TrackTriggeringJob job2;
job2.track = track.get();
job2.threshold = 1.f;
job2.from = 0.f;
job2.to = 1.f;
TrackTriggeringJob::Iterator iterator2;
job2.iterator = &iterator2;
ASSERT_TRUE(job2.Run());
EXPECT_TRUE(iterator2 != iterator);
EXPECT_FALSE(iterator2 == iterator);
}
{ // *
EXPECT_TRUE((*iterator).rising);
TrackTriggeringJob::Edge edge;
EXPECT_ASSERTION(edge = *job.end(), "Can't dereference end iterator");
(void)edge;
}
{ // ->
EXPECT_TRUE(iterator->rising);
bool rising;
EXPECT_ASSERTION(rising = job.end()->rising,
"Can't dereference end iterator");
(void)rising;
}
{ // Pre increment
TrackTriggeringJob::Iterator iterator_cpy1(iterator);
TrackTriggeringJob::Iterator iterator_cpy2(iterator);
++iterator_cpy1;
EXPECT_TRUE(iterator_cpy1 != iterator_cpy2);
EXPECT_FALSE(iterator_cpy1 == iterator_cpy2);
++iterator_cpy2;
EXPECT_TRUE(iterator_cpy1 == iterator_cpy2);
EXPECT_FALSE(iterator_cpy1 != iterator_cpy2);
EXPECT_TRUE(++iterator_cpy1 != iterator_cpy2++);
EXPECT_ASSERTION(++job.end(), "Can't increment end iterator.");
}
{ // Post increment
TrackTriggeringJob::Iterator iterator_cpy1(iterator);
TrackTriggeringJob::Iterator iterator_cpy2(iterator);
iterator_cpy1++;
EXPECT_TRUE(iterator_cpy1 != iterator_cpy2);
EXPECT_FALSE(iterator_cpy1 == iterator_cpy2);
iterator_cpy2++;
EXPECT_TRUE(iterator_cpy1 == iterator_cpy2);
EXPECT_FALSE(iterator_cpy1 != iterator_cpy2);
EXPECT_TRUE((++iterator_cpy1)->rising != (iterator_cpy2++)->rising);
EXPECT_TRUE(iterator_cpy1->rising == iterator_cpy2->rising);
EXPECT_ASSERTION(job.end()++, "Can't increment end iterator.");
}
{ // Assignment
TrackTriggeringJob::Iterator iterator_cpy1(iterator);
TrackTriggeringJob::Iterator iterator_cpy2(iterator);
++iterator_cpy1;
EXPECT_TRUE(iterator_cpy1 != iterator_cpy2);
iterator_cpy2 = iterator_cpy1;
EXPECT_TRUE(iterator_cpy1 == iterator_cpy2);
++iterator_cpy1;
++iterator_cpy2;
EXPECT_TRUE(iterator_cpy1 == iterator_cpy2);
}
{ // Loop
TrackTriggeringJob::Iterator iterator_cpy1(iterator);
TrackTriggeringJob::Iterator iterator_cpy2(iterator);
for (; iterator_cpy1 != job.end(); ++iterator_cpy1, iterator_cpy2++) {
}
EXPECT_TRUE(iterator_cpy2 == job.end());
}
}
TEST(NoRange, TrackEdgeTriggerJob) {
TrackBuilder builder;
ozz::animation::offline::RawFloatTrack raw_track;
// Keyframe values oscillate in range [0,2].
const ozz::animation::offline::RawFloatTrack::Keyframe key0 = {
RawTrackInterpolation::kStep, 0.f, 0.f};
raw_track.keyframes.push_back(key0);
const ozz::animation::offline::RawFloatTrack::Keyframe key1 = {
RawTrackInterpolation::kStep, .5f, 2.f};
raw_track.keyframes.push_back(key1);
const ozz::animation::offline::RawFloatTrack::Keyframe key2 = {
RawTrackInterpolation::kStep, 1.f, 0.f};
raw_track.keyframes.push_back(key2);
// Builds track
ozz::unique_ptr<FloatTrack> track(builder(raw_track));
ASSERT_TRUE(track);
TrackTriggeringJob job;
job.track = track.get();
job.threshold = 1.f;
{ // Forward [0., 0.[
job.from = 0.f;
job.to = 0.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
EXPECT_TRUE(iterator == job.end());
}
{ // Forward [.1, .1]
job.from = .1f;
job.to = .1f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
EXPECT_TRUE(iterator == job.end());
}
{ // Forward [.5, .5[
job.from = .5f;
job.to = .5f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
EXPECT_TRUE(iterator == job.end());
}
{ // Forward [1., 1.]
job.from = 1.f;
job.to = 1.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
EXPECT_TRUE(iterator == job.end());
}
{ // Forward [-.5, -.5[
job.from = -.5f;
job.to = -.5f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
EXPECT_TRUE(iterator == job.end());
}
}
size_t CountEdges(TrackTriggeringJob::Iterator _begin,
const TrackTriggeringJob::Iterator& _end) {
size_t count = 0;
for (; _begin != _end; ++_begin, ++count) {
}
return count;
}
void TestEdgesExpectationBackward(TrackTriggeringJob::Iterator _fw_iterator,
const TrackTriggeringJob& _fw_job) {
// Compute forward edges;
ozz::vector<TrackTriggeringJob::Edge> fw_edges;
for (; _fw_iterator != _fw_job.end(); ++_fw_iterator) {
fw_edges.push_back(*_fw_iterator);
}
// Setup backward job from forward one.
TrackTriggeringJob bw_job = _fw_job;
std::swap(bw_job.from, bw_job.to);
TrackTriggeringJob::Iterator bw_iterator;
bw_job.iterator = &bw_iterator;
ASSERT_TRUE(bw_job.Run());
// Compare forward and backward iterations.
ASSERT_EQ(fw_edges.size(), CountEdges(bw_iterator, bw_job.end()));
for (ozz::vector<TrackTriggeringJob::Edge>::const_reverse_iterator fw_rit =
fw_edges.rbegin();
fw_rit != fw_edges.rend(); ++fw_rit, ++bw_iterator) {
EXPECT_FLOAT_EQ(fw_rit->ratio, bw_iterator->ratio);
EXPECT_EQ(fw_rit->rising, !bw_iterator->rising);
}
}
void TestEdgesExpectation(
const ozz::animation::offline::RawFloatTrack& _raw_track, float _threshold,
const TrackTriggeringJob::Edge* _expected, size_t _size) {
assert(_size >= 2);
// Builds track
ozz::unique_ptr<FloatTrack> track(TrackBuilder()(_raw_track));
ASSERT_TRUE(track);
TrackTriggeringJob job;
job.track = track.get();
job.threshold = _threshold;
{ // Forward [0, 1]
job.from = 0.f;
job.to = 1.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), _size);
TrackTriggeringJob::Iterator it = iterator;
for (size_t i = 0; it != job.end(); ++it, ++i) {
EXPECT_FLOAT_EQ(it->ratio, _expected[i].ratio);
EXPECT_EQ(it->rising, _expected[i].rising);
}
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward [1, 2]
job.from = 1.f;
job.to = 2.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), _size);
TrackTriggeringJob::Iterator it = iterator;
for (size_t i = 0; it != job.end(); ++it, ++i) {
EXPECT_FLOAT_EQ(it->ratio, _expected[i].ratio + 1.f);
EXPECT_EQ(it->rising, _expected[i].rising);
}
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward [0, 3]
job.from = 0.f;
job.to = 3.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), _size * 3);
TrackTriggeringJob::Iterator it = iterator;
for (size_t i = 0; it != job.end(); ++it, ++i) {
const size_t ie = i % _size;
const float loops = static_cast<float>(i / _size);
EXPECT_FLOAT_EQ(it->ratio, _expected[ie].ratio + loops);
EXPECT_EQ(it->rising, _expected[ie].rising);
}
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward, first edge to last, last can be included.
// Last edge is included if it ratio is 1.f.
const bool last_included = _expected[_size - 1].ratio == 1.f;
job.from = _expected[0].ratio;
job.to = _expected[_size - 1].ratio;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()),
last_included ? _size : _size - 1);
TrackTriggeringJob::Iterator it = iterator;
for (size_t i = 0; it != job.end(); ++it, ++i) {
EXPECT_FLOAT_EQ(it->ratio, _expected[i].ratio);
EXPECT_EQ(it->rising, _expected[i].rising);
}
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward, after first edge to 1.
job.from = nexttowardf(_expected[0].ratio, 1.f);
job.to = 1.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), _size - 1);
TrackTriggeringJob::Iterator it = iterator;
for (size_t i = 0; it != job.end(); ++it, ++i) {
EXPECT_FLOAT_EQ(it->ratio, _expected[i + 1].ratio);
EXPECT_EQ(it->rising, _expected[i + 1].rising);
}
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward, 0 to first edge.
job.from = 0.f;
job.to = _expected[0].ratio;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), 0u);
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward, 0 to after first edge.
job.from = 0.f;
job.to = nexttowardf(_expected[0].ratio, 1.f);
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), 1u);
EXPECT_FLOAT_EQ(iterator->ratio, _expected[0].ratio);
EXPECT_EQ(iterator->rising, _expected[0].rising);
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward, 0 to before last edge.
job.from = 0.f;
job.to = nexttowardf(_expected[_size - 1].ratio, 0.f);
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), _size - 1);
TrackTriggeringJob::Iterator it = iterator;
for (size_t i = 0; it != job.end(); ++it, ++i) {
EXPECT_FLOAT_EQ(it->ratio, _expected[i].ratio);
EXPECT_EQ(it->rising, _expected[i].rising);
}
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward, 0 to after last edge.
job.from = 0.f;
job.to = nexttowardf(_expected[_size - 1].ratio, 1.f);
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), _size);
TrackTriggeringJob::Iterator it = iterator;
for (size_t i = 0; it != job.end(); ++it, ++i) {
EXPECT_FLOAT_EQ(it->ratio, _expected[i].ratio);
EXPECT_EQ(it->rising, _expected[i].rising);
}
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward, 1 to after last edge + 1.
job.from = 1.f;
job.to = nexttowardf(_expected[_size - 1].ratio + 1.f, 2.f);
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), _size);
TrackTriggeringJob::Iterator it = iterator;
for (size_t i = 0; it != job.end(); ++it, ++i) {
EXPECT_FLOAT_EQ(it->ratio, _expected[i].ratio + 1.f);
EXPECT_EQ(it->rising, _expected[i].rising);
}
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward, 46 to after last edge + 46.
job.from = 46.f;
job.to = nexttowardf(_expected[_size - 1].ratio + 46.f, 100.f);
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), _size);
TrackTriggeringJob::Iterator it = iterator;
for (size_t i = 0; it != job.end(); ++it, ++i) {
EXPECT_FLOAT_EQ(it->ratio, _expected[i].ratio + 46.f);
EXPECT_EQ(it->rising, _expected[i].rising);
}
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward, 46 to before last edge + 46.
job.from = 46.f;
job.to = nexttowardf(_expected[_size - 1].ratio + 46.f, -100.f);
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), _size - 1);
TrackTriggeringJob::Iterator it = iterator;
for (size_t i = 0; it != job.end(); ++it, ++i) {
EXPECT_FLOAT_EQ(it->ratio, _expected[i].ratio + 46.f);
EXPECT_EQ(it->rising, _expected[i].rising);
}
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward, 46 to last edge + 46.
const bool last_included = _expected[_size - 1].ratio == 1.f;
job.from = 46.f;
job.to = _expected[_size - 1].ratio + 46.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()),
last_included ? _size : _size - 1);
TrackTriggeringJob::Iterator it = iterator;
for (size_t i = 0; it != job.end(); ++it, ++i) {
EXPECT_FLOAT_EQ(it->ratio, _expected[i].ratio + 46.f);
EXPECT_EQ(it->rising, _expected[i].rising);
}
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward, 0 to before last edge + 1
const bool last_included = _expected[_size - 1].ratio == 1.f;
job.from = 0.f;
job.to = _expected[_size - 1].ratio + 1.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()),
last_included ? _size * 2 : _size * 2 - 1);
TrackTriggeringJob::Iterator it = iterator;
for (size_t i = 0; i < _size; ++i, ++it) {
EXPECT_FLOAT_EQ(it->ratio, _expected[i].ratio);
EXPECT_EQ(it->rising, _expected[i].rising);
}
for (size_t i = _size; i < (last_included ? _size * 2 : _size * 2 - 1);
++i, ++it) {
EXPECT_FLOAT_EQ(it->ratio, _expected[i - _size].ratio + 1.f);
EXPECT_EQ(it->rising, _expected[i - _size].rising);
}
TestEdgesExpectationBackward(iterator, job);
}
// Negative times
{ // Forward [-1, 0]
job.from = -1.f;
job.to = 0.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), _size);
TrackTriggeringJob::Iterator it = iterator;
for (size_t i = 0; it != job.end(); ++it, ++i) {
EXPECT_FLOAT_EQ(it->ratio, _expected[i].ratio - 1.f);
EXPECT_EQ(it->rising, _expected[i].rising);
}
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward [-2, -1]
job.from = -2.f;
job.to = -1.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), _size);
TrackTriggeringJob::Iterator it = iterator;
for (size_t i = 0; it != job.end(); ++it, ++i) {
EXPECT_FLOAT_EQ(it->ratio, _expected[i].ratio - 2.f);
EXPECT_EQ(it->rising, _expected[i].rising);
}
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward [-1, 1]
job.from = -1.f;
job.to = 1.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), _size * 2);
TrackTriggeringJob::Iterator it = iterator;
for (size_t i = 0; i < _size; ++i, ++it) {
EXPECT_FLOAT_EQ(it->ratio, _expected[i].ratio - 1.f);
EXPECT_EQ(it->rising, _expected[i].rising);
}
for (size_t i = 0; i < _size; ++i, ++it) {
EXPECT_FLOAT_EQ(it->ratio, _expected[i].ratio);
EXPECT_EQ(it->rising, _expected[i].rising);
}
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward, -1 to first edge.
job.from = -1.f;
job.to = _expected[0].ratio - 1.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), 0u);
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward, -1 to after last edge.
job.from = -1.f;
job.to = _expected[_size - 1].ratio - .999999f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), _size);
TrackTriggeringJob::Iterator it = iterator;
for (size_t i = 0; it != job.end(); ++it, ++i) {
EXPECT_FLOAT_EQ(it->ratio, _expected[i].ratio - 1.f);
EXPECT_EQ(it->rising, _expected[i].rising);
}
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward [-1, -eps]
// Last edge is included if it ratio is not 1.f.
const bool last_included = _expected[_size - 1].ratio != 1.f;
job.from = -1.f;
job.to = nexttowardf(0.f, -1.f);
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()),
last_included ? _size : _size - 1);
TrackTriggeringJob::Iterator it = iterator;
for (size_t i = 0; it != job.end(); ++it, ++i) {
EXPECT_FLOAT_EQ(it->ratio, _expected[i].ratio - 1.f);
EXPECT_EQ(it->rising, _expected[i].rising);
}
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward [-eps, ..]
// Last edge is included if it ratio is 1.f.
const bool last_included = _expected[_size - 1].ratio == 1.f;
job.from = nexttowardf(0.f, -1.f);
job.to = nexttowardf(_expected[0].ratio, 1.f);
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
if (last_included) {
ASSERT_EQ(CountEdges(iterator, job.end()), 2u);
TrackTriggeringJob::Iterator it = iterator;
EXPECT_FLOAT_EQ(it->ratio, _expected[_size - 1].ratio - 1.f);
EXPECT_EQ(it->rising, _expected[_size - 1].rising);
++it;
EXPECT_FLOAT_EQ(it->ratio, _expected[0].ratio);
EXPECT_EQ(it->rising, _expected[0].rising);
} else {
ASSERT_EQ(CountEdges(iterator, job.end()), 1u);
TrackTriggeringJob::Iterator it = iterator;
EXPECT_FLOAT_EQ(it->ratio, _expected[0].ratio);
EXPECT_EQ(it->rising, _expected[0].rising);
}
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward, -46 to after last edge + -46.
job.from = -46.f;
job.to = nexttowardf(_expected[_size - 1].ratio + -46.f, 100.f);
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), _size);
TrackTriggeringJob::Iterator it = iterator;
for (size_t i = 0; it != job.end(); ++it, ++i) {
EXPECT_FLOAT_EQ(it->ratio, _expected[i].ratio + -46.f);
EXPECT_EQ(it->rising, _expected[i].rising);
}
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward, -46 to before last edge + -46.
job.from = -46.f;
job.to = nexttowardf(_expected[_size - 1].ratio + -46.f, -100.f);
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), _size - 1);
TrackTriggeringJob::Iterator it = iterator;
for (size_t i = 0; it != job.end(); ++it, ++i) {
EXPECT_FLOAT_EQ(it->ratio, _expected[i].ratio + -46.f);
EXPECT_EQ(it->rising, _expected[i].rising);
}
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward, -46 to last edge + -46.
const bool last_included = _expected[_size - 1].ratio == 1.f;
job.from = -46.f;
job.to = _expected[_size - 1].ratio + -46.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()),
last_included ? _size : _size - 1);
TrackTriggeringJob::Iterator it = iterator;
for (size_t i = 0; it != job.end(); ++it, ++i) {
EXPECT_FLOAT_EQ(it->ratio, _expected[i].ratio + -46.f);
EXPECT_EQ(it->rising, _expected[i].rising);
}
TestEdgesExpectationBackward(iterator, job);
}
{ // Forward [-21.3999996, -21.0000019]
job.from = -21.3999996f;
job.to = -21.0000019f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
size_t found = 0;
for (size_t i = 0; i < _size; ++i) {
if (_expected[i].ratio - 22.f >= job.from &&
_expected[i].ratio - 22.f < job.to) {
found++;
}
}
ASSERT_EQ(CountEdges(iterator, job.end()), found);
TestEdgesExpectationBackward(iterator, job);
}
{ // Randomized tests forward/backward coherency
const float kMaxRange = 10.f;
const size_t kMaxIterations = 1000;
for (size_t i = 0; i < kMaxIterations; ++i) {
job.from =
kMaxRange * (1.f - 2.f * static_cast<float>(rand()) /
static_cast<float>(RAND_MAX));
job.to = kMaxRange * (1.f - 2.f * static_cast<float>(rand()) /
static_cast<float>(RAND_MAX));
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
TestEdgesExpectationBackward(iterator, job);
}
}
{ // Randomized tests rising/falling coherency
const float kMaxRange = 2.f;
const size_t kMaxIterations = 1000;
float ratio = 0.f;
bool rising = false;
bool init = false;
for (size_t i = 0; i < kMaxIterations; ++i) {
// Finds new evaluation range
float new_time =
ratio +
kMaxRange * (1.f - 2.f * static_cast<float>(rand()) /
static_cast<float>(RAND_MAX));
switch (rand() % 20) {
case 0: {
// Set ratio to a keyframe ratio.
new_time = _expected[rand() % _size].ratio + floorf(new_time);
break;
}
case 1: {
// Set ratio to after a keyframe ratio.
new_time = nexttowardf(
_expected[rand() % _size].ratio + floorf(new_time), 1e15f);
break;
}
case 2: {
// Set ratio to before a keyframe ratio.
new_time = nexttowardf(
_expected[rand() % _size].ratio + floorf(new_time), -1e15f);
break;
}
default:
break;
}
job.from = ratio;
ratio = new_time;
job.to = ratio;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
// Successive edges should always be opposed, whichever direction the
// ratio is going.
for (const TrackTriggeringJob::Iterator end = job.end(); iterator != end;
++iterator) {
const TrackTriggeringJob::Edge& edge = *iterator;
if (!init) {
rising = edge.rising;
init = true;
} else {
// if (rising == edges[e].rising) {
// assert(false);
// break;
// }
ASSERT_TRUE(rising != edge.rising);
rising = edge.rising;
}
}
}
}
}
template <size_t _size>
inline void TestEdgesExpectation(
const ozz::animation::offline::RawFloatTrack& _raw_track, float _threshold,
const TrackTriggeringJob::Edge (&_expected)[_size]) {
static_assert(_size >= 2, "Minimum 2 edges.");
TestEdgesExpectation(_raw_track, _threshold, _expected, _size);
}
TEST(SquareStep, TrackEdgeTriggerJob) {
{ // Rising edge at t = 0.5
ozz::animation::offline::RawFloatTrack raw_track;
// Keyframe values oscillate in range [0,2].
const ozz::animation::offline::RawFloatTrack::Keyframe key0 = {
RawTrackInterpolation::kStep, 0.f, 0.f};
raw_track.keyframes.push_back(key0);
const ozz::animation::offline::RawFloatTrack::Keyframe key1 = {
RawTrackInterpolation::kStep, .5f, 2.f};
raw_track.keyframes.push_back(key1);
const ozz::animation::offline::RawFloatTrack::Keyframe key2 = {
RawTrackInterpolation::kStep, 1.f, 0.f};
raw_track.keyframes.push_back(key2);
const TrackTriggeringJob::Edge expected[] = {{.5f, true}, {1.f, false}};
TestEdgesExpectation(raw_track, 0.f, expected);
TestEdgesExpectation(raw_track, 1.f, expected);
}
{ // Rising edge at t = 0.6, no falling edge at end
ozz::animation::offline::RawFloatTrack raw_track;
// Keyframe values oscillate in range [0,2].
const ozz::animation::offline::RawFloatTrack::Keyframe key0 = {
RawTrackInterpolation::kStep, 0.f, 0.f};
raw_track.keyframes.push_back(key0);
const ozz::animation::offline::RawFloatTrack::Keyframe key1 = {
RawTrackInterpolation::kStep, .6f, 2.f};
raw_track.keyframes.push_back(key1);
const TrackTriggeringJob::Edge expected[] = {{0.f, false}, {.6f, true}};
TestEdgesExpectation(raw_track, 0.f, expected);
TestEdgesExpectation(raw_track, 1.f, expected);
}
{ // Falling edge at t = 0.5
ozz::animation::offline::RawFloatTrack raw_track;
// Keyframe values oscillate in range [0,2].
const ozz::animation::offline::RawFloatTrack::Keyframe key0 = {
RawTrackInterpolation::kStep, 0.f, 2.f};
raw_track.keyframes.push_back(key0);
const ozz::animation::offline::RawFloatTrack::Keyframe key1 = {
RawTrackInterpolation::kStep, .5f, 0.f};
raw_track.keyframes.push_back(key1);
const TrackTriggeringJob::Edge expected[] = {{0.f, true}, {.5f, false}};
TestEdgesExpectation(raw_track, 0.f, expected);
TestEdgesExpectation(raw_track, 1.f, expected);
}
{ // Negative values
ozz::animation::offline::RawFloatTrack raw_track;
// Keyframe values oscillate in range [-1,1].
const ozz::animation::offline::RawFloatTrack::Keyframe key0 = {
RawTrackInterpolation::kStep, 0.f, -1.f};
raw_track.keyframes.push_back(key0);
const ozz::animation::offline::RawFloatTrack::Keyframe key1 = {
RawTrackInterpolation::kStep, .5f, 1.f};
raw_track.keyframes.push_back(key1);
const ozz::animation::offline::RawFloatTrack::Keyframe key2 = {
RawTrackInterpolation::kStep, 1.f, -1.f};
raw_track.keyframes.push_back(key2);
const TrackTriggeringJob::Edge expected[] = {{.5f, true}, {1.f, false}};
TestEdgesExpectation(raw_track, 0.f, expected);
TestEdgesExpectation(raw_track, -1.f, expected);
}
{ // More edges
ozz::animation::offline::RawFloatTrack raw_track;
const ozz::animation::offline::RawFloatTrack::Keyframe key0 = {
RawTrackInterpolation::kStep, .0f, 0.f};
raw_track.keyframes.push_back(key0);
const ozz::animation::offline::RawFloatTrack::Keyframe key1 = {
RawTrackInterpolation::kStep, .2f, 2.f};
raw_track.keyframes.push_back(key1);
const ozz::animation::offline::RawFloatTrack::Keyframe key2 = {
RawTrackInterpolation::kStep, .3f, 0.f};
raw_track.keyframes.push_back(key2);
const ozz::animation::offline::RawFloatTrack::Keyframe key3 = {
RawTrackInterpolation::kStep, .4f, 1.f};
raw_track.keyframes.push_back(key3);
const ozz::animation::offline::RawFloatTrack::Keyframe key4 = {
RawTrackInterpolation::kStep, .5f, 0.f};
raw_track.keyframes.push_back(key4);
const TrackTriggeringJob::Edge expected0[] = {
{.2f, true}, {.3f, false}, {.4f, true}, {.5f, false}};
TestEdgesExpectation(raw_track, 0.f, expected0);
const TrackTriggeringJob::Edge expected1[] = {{.2f, true}, {.3f, false}};
TestEdgesExpectation(raw_track, 1.f, expected1);
}
}
TEST(Linear, TrackEdgeTriggerJob) {
{ // Higher point at t = 0.5
ozz::animation::offline::RawFloatTrack raw_track;
// Keyframe values oscillate in range [0,2].
const ozz::animation::offline::RawFloatTrack::Keyframe key0 = {
RawTrackInterpolation::kLinear, 0.f, 0.f};
raw_track.keyframes.push_back(key0);
const ozz::animation::offline::RawFloatTrack::Keyframe key1 = {
RawTrackInterpolation::kLinear, .5f, 2.f};
raw_track.keyframes.push_back(key1);
const ozz::animation::offline::RawFloatTrack::Keyframe key2 = {
RawTrackInterpolation::kLinear, 1.f, 0.f};
raw_track.keyframes.push_back(key2);
const TrackTriggeringJob::Edge expected0[] = {{.25f, true}, {.75f, false}};
TestEdgesExpectation(raw_track, 1.f, expected0);
const TrackTriggeringJob::Edge expected1[] = {{.125f, true},
{.875f, false}};
TestEdgesExpectation(raw_track, .5f, expected1);
const TrackTriggeringJob::Edge expected2[] = {{.375f, true},
{.625f, false}};
TestEdgesExpectation(raw_track, 1.5f, expected2);
}
{ // Higher point at t = 0.5
ozz::animation::offline::RawFloatTrack raw_track;
// Keyframe values oscillate in range [0,2].
const ozz::animation::offline::RawFloatTrack::Keyframe key0 = {
RawTrackInterpolation::kLinear, 0.f, 0.f};
raw_track.keyframes.push_back(key0);
const ozz::animation::offline::RawFloatTrack::Keyframe key1 = {
RawTrackInterpolation::kLinear, .5f, 2.f};
raw_track.keyframes.push_back(key1);
const TrackTriggeringJob::Edge expected[] = {{0.f, false}, {.25f, true}};
TestEdgesExpectation(raw_track, 1.f, expected);
}
{ // Negative values
ozz::animation::offline::RawFloatTrack raw_track;
// Keyframe values oscillate in range [-1,1].
const ozz::animation::offline::RawFloatTrack::Keyframe key0 = {
RawTrackInterpolation::kLinear, 0.f, -1.f};
raw_track.keyframes.push_back(key0);
const ozz::animation::offline::RawFloatTrack::Keyframe key1 = {
RawTrackInterpolation::kLinear, .5f, 1.f};
raw_track.keyframes.push_back(key1);
const TrackTriggeringJob::Edge expected[] = {{0.f, false}, {.25f, true}};
TestEdgesExpectation(raw_track, 0.f, expected);
}
}
TEST(Mixed, TrackEdgeTriggerJob) {
{ // Higher point at t = 0.5
ozz::animation::offline::RawFloatTrack raw_track;
// Keyframe values oscillate in range [0,2].
const ozz::animation::offline::RawFloatTrack::Keyframe key0 = {
RawTrackInterpolation::kStep, 0.f, 0.f};
raw_track.keyframes.push_back(key0);
const ozz::animation::offline::RawFloatTrack::Keyframe key1 = {
RawTrackInterpolation::kLinear, .5f, 2.f};
raw_track.keyframes.push_back(key1);
const ozz::animation::offline::RawFloatTrack::Keyframe key2 = {
RawTrackInterpolation::kLinear, 1.f, 0.f};
raw_track.keyframes.push_back(key2);
const TrackTriggeringJob::Edge expected0[] = {{.5f, true}, {.75f, false}};
TestEdgesExpectation(raw_track, 1.f, expected0);
const TrackTriggeringJob::Edge expected1[] = {{.5f, true}, {.875f, false}};
TestEdgesExpectation(raw_track, .5f, expected1);
const TrackTriggeringJob::Edge expected2[] = {{.5f, true}, {.625f, false}};
TestEdgesExpectation(raw_track, 1.5f, expected2);
}
{ // Higher point at t = 0.5
ozz::animation::offline::RawFloatTrack raw_track;
// Keyframe values oscillate in range [0,2].
const ozz::animation::offline::RawFloatTrack::Keyframe key0 = {
RawTrackInterpolation::kLinear, 0.f, 0.f};
raw_track.keyframes.push_back(key0);
const ozz::animation::offline::RawFloatTrack::Keyframe key1 = {
RawTrackInterpolation::kStep, .5f, 2.f};
raw_track.keyframes.push_back(key1);
const ozz::animation::offline::RawFloatTrack::Keyframe key2 = {
RawTrackInterpolation::kLinear, 1.f, 0.f};
raw_track.keyframes.push_back(key2);
const TrackTriggeringJob::Edge expected0[] = {{.25f, true}, {1.f, false}};
TestEdgesExpectation(raw_track, 1.f, expected0);
const TrackTriggeringJob::Edge expected1[] = {{.125f, true}, {1.f, false}};
TestEdgesExpectation(raw_track, .5f, expected1);
const TrackTriggeringJob::Edge expected2[] = {{.375f, true}, {1.f, false}};
TestEdgesExpectation(raw_track, 1.5f, expected2);
}
}
TEST(StepThreshold, TrackEdgeTriggerJob) {
TrackBuilder builder;
// Rising edge at t = 0.5
ozz::animation::offline::RawFloatTrack raw_track;
// Keyframe values oscillate in range [-1,1].
const ozz::animation::offline::RawFloatTrack::Keyframe key0 = {
RawTrackInterpolation::kStep, 0.f, -1.f};
raw_track.keyframes.push_back(key0);
const ozz::animation::offline::RawFloatTrack::Keyframe key1 = {
RawTrackInterpolation::kStep, .5f, 1.f};
raw_track.keyframes.push_back(key1);
const ozz::animation::offline::RawFloatTrack::Keyframe key2 = {
RawTrackInterpolation::kStep, 1.f, -1.f};
raw_track.keyframes.push_back(key2);
// Builds track
ozz::unique_ptr<FloatTrack> track(builder(raw_track));
ASSERT_TRUE(track);
TrackTriggeringJob job;
job.track = track.get();
job.from = 0.f;
job.to = 1.f;
{ // In range
job.threshold = .5f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), 2u);
EXPECT_FLOAT_EQ(iterator->ratio, .5f);
EXPECT_EQ(iterator->rising, true);
++iterator;
EXPECT_FLOAT_EQ(iterator->ratio, 1.f);
EXPECT_EQ(iterator->rising, false);
}
{ // Bottom of range is included
job.threshold = -1.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), 2u);
EXPECT_FLOAT_EQ(iterator->ratio, .5f);
EXPECT_EQ(iterator->rising, true);
++iterator;
EXPECT_FLOAT_EQ(iterator->ratio, 1.f);
EXPECT_EQ(iterator->rising, false);
}
{ // Top range is excluded
job.threshold = 1.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), 0u);
}
{ // In range
job.threshold = 0.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), 2u);
EXPECT_FLOAT_EQ(iterator->ratio, .5f);
EXPECT_EQ(iterator->rising, true);
++iterator;
EXPECT_FLOAT_EQ(iterator->ratio, 1.f);
EXPECT_EQ(iterator->rising, false);
}
{ // Out of range
job.from = 0.f;
job.to = 1.f;
job.threshold = 2.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), 0u);
}
}
TEST(StepThresholdBool, TrackEdgeTriggerJob) {
TrackBuilder builder;
// Rising edge at t = 0.5
ozz::animation::offline::RawFloatTrack raw_track;
// Keyframe values oscillate in range [0,1].
const ozz::animation::offline::RawFloatTrack::Keyframe key0 = {
RawTrackInterpolation::kStep, 0.f, 0.f};
raw_track.keyframes.push_back(key0);
const ozz::animation::offline::RawFloatTrack::Keyframe key1 = {
RawTrackInterpolation::kStep, .5f, 1.f};
raw_track.keyframes.push_back(key1);
const ozz::animation::offline::RawFloatTrack::Keyframe key2 = {
RawTrackInterpolation::kStep, 1.f, 0.f};
raw_track.keyframes.push_back(key2);
// Builds track
ozz::unique_ptr<FloatTrack> track(builder(raw_track));
ASSERT_TRUE(track);
TrackTriggeringJob job;
job.track = track.get();
job.from = 0.f;
job.to = 1.f;
{ // In range
job.threshold = .5f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), 2u);
EXPECT_FLOAT_EQ(iterator->ratio, .5f);
EXPECT_EQ(iterator->rising, true);
++iterator;
EXPECT_FLOAT_EQ(iterator->ratio, 1.f);
EXPECT_EQ(iterator->rising, false);
}
{ // Top range is excluded
job.threshold = 1.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), 0u);
}
{ // Bottom range is included
job.threshold = 0.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), 2u);
EXPECT_FLOAT_EQ(iterator->ratio, .5f);
EXPECT_EQ(iterator->rising, true);
++iterator;
EXPECT_FLOAT_EQ(iterator->ratio, 1.f);
EXPECT_EQ(iterator->rising, false);
}
}
TEST(LinearThreshold, TrackEdgeTriggerJob) {
TrackBuilder builder;
// Rising edge at t = 0.5
ozz::animation::offline::RawFloatTrack raw_track;
// Keyframe values oscillate in range [-1,1].
const ozz::animation::offline::RawFloatTrack::Keyframe key0 = {
RawTrackInterpolation::kLinear, 0.f, -1.f};
raw_track.keyframes.push_back(key0);
const ozz::animation::offline::RawFloatTrack::Keyframe key1 = {
RawTrackInterpolation::kLinear, .5f, 1.f};
raw_track.keyframes.push_back(key1);
const ozz::animation::offline::RawFloatTrack::Keyframe key2 = {
RawTrackInterpolation::kLinear, 1.f, -1.f};
raw_track.keyframes.push_back(key2);
// Builds track
ozz::unique_ptr<FloatTrack> track(builder(raw_track));
ASSERT_TRUE(track);
TrackTriggeringJob job;
job.track = track.get();
job.from = 0.f;
job.to = 1.f;
{ // In range
job.threshold = .5f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), 2u);
EXPECT_FLOAT_EQ(iterator->ratio, .375f);
EXPECT_EQ(iterator->rising, true);
++iterator;
EXPECT_FLOAT_EQ(iterator->ratio, .625f);
EXPECT_EQ(iterator->rising, false);
}
{ // Top range is excluded
job.threshold = 1.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), 0u);
}
{ // In range
job.threshold = 0.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), 2u);
EXPECT_FLOAT_EQ(iterator->ratio, .25f);
EXPECT_EQ(iterator->rising, true);
++iterator;
EXPECT_FLOAT_EQ(iterator->ratio, .75f);
EXPECT_EQ(iterator->rising, false);
}
{ // Bottom of range is included
job.threshold = -1.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), 2u);
EXPECT_FLOAT_EQ(iterator->ratio, 0.f);
EXPECT_EQ(iterator->rising, true);
++iterator;
EXPECT_FLOAT_EQ(iterator->ratio, 1.f);
EXPECT_EQ(iterator->rising, false);
}
{ // Out of range
job.from = 0.f;
job.to = 1.f;
job.threshold = 2.f;
TrackTriggeringJob::Iterator iterator;
job.iterator = &iterator;
ASSERT_TRUE(job.Run());
ASSERT_EQ(CountEdges(iterator, job.end()), 0u);
}
}