You've already forked libopenshot
mirror of
https://github.com/OpenShot/libopenshot.git
synced 2026-03-02 08:53:52 -08:00
Merge branch 'develop' into hardware-support
This commit is contained in:
@@ -127,7 +127,7 @@ namespace openshot {
|
||||
std::shared_ptr<Frame> GetOrCreateFrame(int64_t number);
|
||||
|
||||
/// Adjust the audio and image of a time mapped frame
|
||||
std::shared_ptr<Frame> get_time_mapped_frame(std::shared_ptr<Frame> frame, int64_t frame_number);
|
||||
void get_time_mapped_frame(std::shared_ptr<Frame> frame, int64_t frame_number);
|
||||
|
||||
/// Init default settings for a clip
|
||||
void init_settings();
|
||||
|
||||
@@ -52,11 +52,6 @@ namespace openshot {
|
||||
* \endcode
|
||||
*/
|
||||
class Coordinate {
|
||||
private:
|
||||
bool increasing; ///< Is the Y value increasing or decreasing?
|
||||
Fraction repeated; ///< Fraction of repeated Y values (for example, 1/3 would be the first Y value of 3 repeated values)
|
||||
double delta; ///< This difference in Y value (from the previous unique Y value)
|
||||
|
||||
public:
|
||||
double X; ///< The X value of the coordinate (usually representing the frame #)
|
||||
double Y; ///< The Y value of the coordinate (usually representing the value of the property being animated)
|
||||
@@ -69,27 +64,6 @@ namespace openshot {
|
||||
/// @param y The Y coordinate (usually representing the value of the property being animated)
|
||||
Coordinate(double x, double y);
|
||||
|
||||
/// @brief Set the repeating Fraction (used internally on the timeline, to track changes to coordinates)
|
||||
/// @param is_repeated The fraction representing how many times this coordinate Y value repeats (only used on the timeline)
|
||||
void Repeat(Fraction is_repeated) { repeated=is_repeated; }
|
||||
|
||||
/// Get the repeating Fraction (used internally on the timeline, to track changes to coordinates)
|
||||
Fraction Repeat() { return repeated; }
|
||||
|
||||
/// @brief Set the increasing flag (used internally on the timeline, to track changes to coordinates)
|
||||
/// @param is_increasing Indicates if this coordinate Y value is increasing (when compared to the previous coordinate)
|
||||
void IsIncreasing(bool is_increasing) { increasing = is_increasing; }
|
||||
|
||||
/// Get the increasing flag (used internally on the timeline, to track changes to coordinates)
|
||||
bool IsIncreasing() { return increasing; }
|
||||
|
||||
/// @brief Set the delta / difference between previous coordinate value (used internally on the timeline, to track changes to coordinates)
|
||||
/// @param new_delta Indicates how much this Y value differs from the previous Y value
|
||||
void Delta(double new_delta) { delta=new_delta; }
|
||||
|
||||
/// Get the delta / difference between previous coordinate value (used internally on the timeline, to track changes to coordinates)
|
||||
float Delta() { return delta; }
|
||||
|
||||
/// Get and Set JSON methods
|
||||
string Json(); ///< Generate JSON string of this object
|
||||
Json::Value JsonValue(); ///< Generate Json::JsonValue for this object
|
||||
|
||||
32
src/Clip.cpp
32
src/Clip.cpp
@@ -340,13 +340,13 @@ std::shared_ptr<Frame> Clip::GetFrame(int64_t requested_frame)
|
||||
frame->AddAudio(true, channel, 0, original_frame->GetAudioSamples(channel), original_frame->GetAudioSamplesCount(), 1.0);
|
||||
|
||||
// Get time mapped frame number (used to increase speed, change direction, etc...)
|
||||
std::shared_ptr<Frame> new_frame = get_time_mapped_frame(frame, requested_frame);
|
||||
get_time_mapped_frame(frame, requested_frame);
|
||||
|
||||
// Apply effects to the frame (if any)
|
||||
apply_effects(new_frame);
|
||||
apply_effects(frame);
|
||||
|
||||
// Return processed 'frame'
|
||||
return new_frame;
|
||||
return frame;
|
||||
}
|
||||
else
|
||||
// Throw error if reader not initialized
|
||||
@@ -389,7 +389,7 @@ void Clip::reverse_buffer(juce::AudioSampleBuffer* buffer)
|
||||
}
|
||||
|
||||
// Adjust the audio and image of a time mapped frame
|
||||
std::shared_ptr<Frame> Clip::get_time_mapped_frame(std::shared_ptr<Frame> frame, int64_t frame_number)
|
||||
void Clip::get_time_mapped_frame(std::shared_ptr<Frame> frame, int64_t frame_number)
|
||||
{
|
||||
// Check for valid reader
|
||||
if (!reader)
|
||||
@@ -400,7 +400,6 @@ std::shared_ptr<Frame> Clip::get_time_mapped_frame(std::shared_ptr<Frame> frame,
|
||||
if (time.Values.size() > 1)
|
||||
{
|
||||
const GenericScopedLock<CriticalSection> lock(getFrameCriticalSection);
|
||||
std::shared_ptr<Frame> new_frame;
|
||||
|
||||
// create buffer and resampler
|
||||
juce::AudioSampleBuffer *samples = NULL;
|
||||
@@ -408,14 +407,7 @@ std::shared_ptr<Frame> Clip::get_time_mapped_frame(std::shared_ptr<Frame> frame,
|
||||
resampler = new AudioResampler();
|
||||
|
||||
// Get new frame number
|
||||
int new_frame_number = adjust_frame_number_minimum(round(time.GetValue(frame_number)));
|
||||
|
||||
// Create a new frame
|
||||
int samples_in_frame = Frame::GetSamplesPerFrame(new_frame_number, reader->info.fps, reader->info.sample_rate, frame->GetAudioChannelsCount());
|
||||
new_frame = std::make_shared<Frame>(new_frame_number, 1, 1, "#000000", samples_in_frame, frame->GetAudioChannelsCount());
|
||||
|
||||
// Copy the image from the new frame
|
||||
new_frame->AddImage(std::shared_ptr<QImage>(new QImage(*GetOrCreateFrame(new_frame_number)->GetImage())));
|
||||
int new_frame_number = frame->number;
|
||||
|
||||
// Get delta (difference in previous Y value)
|
||||
int delta = int(round(time.GetDelta(frame_number)));
|
||||
@@ -463,7 +455,7 @@ std::shared_ptr<Frame> Clip::get_time_mapped_frame(std::shared_ptr<Frame> frame,
|
||||
start -= 1;
|
||||
for (int channel = 0; channel < channels; channel++)
|
||||
// Add new (slower) samples, to the frame object
|
||||
new_frame->AddAudio(true, channel, 0, resampled_buffer->getReadPointer(channel, start),
|
||||
frame->AddAudio(true, channel, 0, resampled_buffer->getReadPointer(channel, start),
|
||||
number_of_samples, 1.0f);
|
||||
|
||||
// Clean up
|
||||
@@ -571,7 +563,7 @@ std::shared_ptr<Frame> Clip::get_time_mapped_frame(std::shared_ptr<Frame> frame,
|
||||
// Add the newly resized audio samples to the current frame
|
||||
for (int channel = 0; channel < channels; channel++)
|
||||
// Add new (slower) samples, to the frame object
|
||||
new_frame->AddAudio(true, channel, 0, buffer->getReadPointer(channel), number_of_samples, 1.0f);
|
||||
frame->AddAudio(true, channel, 0, buffer->getReadPointer(channel), number_of_samples, 1.0f);
|
||||
|
||||
// Clean up
|
||||
buffer = NULL;
|
||||
@@ -592,7 +584,7 @@ std::shared_ptr<Frame> Clip::get_time_mapped_frame(std::shared_ptr<Frame> frame,
|
||||
|
||||
// Add reversed samples to the frame object
|
||||
for (int channel = 0; channel < channels; channel++)
|
||||
new_frame->AddAudio(true, channel, 0, samples->getReadPointer(channel), number_of_samples, 1.0f);
|
||||
frame->AddAudio(true, channel, 0, samples->getReadPointer(channel), number_of_samples, 1.0f);
|
||||
|
||||
|
||||
}
|
||||
@@ -600,13 +592,7 @@ std::shared_ptr<Frame> Clip::get_time_mapped_frame(std::shared_ptr<Frame> frame,
|
||||
delete samples;
|
||||
samples = NULL;
|
||||
}
|
||||
|
||||
// Return new time mapped frame
|
||||
return new_frame;
|
||||
|
||||
} else
|
||||
// Use original frame
|
||||
return frame;
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust frame number minimum value
|
||||
|
||||
@@ -32,12 +32,12 @@ using namespace openshot;
|
||||
|
||||
// Default constructor for a coordinate, which defaults the X and Y to zero (0,0)
|
||||
Coordinate::Coordinate() :
|
||||
X(0), Y(0), increasing(true), repeated(1,1), delta(0.0) {
|
||||
X(0), Y(0) {
|
||||
}
|
||||
|
||||
// Constructor which also allows the user to set the X and Y
|
||||
Coordinate::Coordinate(double x, double y) :
|
||||
X(x), Y(y), increasing(true), repeated(1,1), delta(0.0) {
|
||||
X(x), Y(y) {
|
||||
}
|
||||
|
||||
|
||||
@@ -96,15 +96,4 @@ void Coordinate::SetJsonValue(Json::Value root) {
|
||||
X = root["X"].asDouble();
|
||||
if (!root["Y"].isNull())
|
||||
Y = root["Y"].asDouble();
|
||||
if (!root["increasing"].isNull())
|
||||
increasing = root["increasing"].asBool();
|
||||
if (!root["repeated"].isNull() && root["repeated"].isObject())
|
||||
{
|
||||
if (!root["repeated"]["num"].isNull())
|
||||
repeated.num = root["repeated"]["num"].asInt();
|
||||
if (!root["repeated"]["den"].isNull())
|
||||
repeated.den = root["repeated"]["den"].asInt();
|
||||
}
|
||||
if (!root["delta"].isNull())
|
||||
delta = root["delta"].asDouble();
|
||||
}
|
||||
|
||||
@@ -54,9 +54,6 @@ FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType targe
|
||||
|
||||
// Adjust cache size based on size of frame and audio
|
||||
final_cache.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels);
|
||||
|
||||
// init mapping between original and target frames
|
||||
Init();
|
||||
}
|
||||
|
||||
// Destructor
|
||||
@@ -205,22 +202,23 @@ void FrameMapper::Init()
|
||||
}
|
||||
|
||||
} else {
|
||||
// Map the remaining framerates using a simple Keyframe curve
|
||||
// Calculate the difference (to be used as a multiplier)
|
||||
// Map the remaining framerates using a linear algorithm
|
||||
double rate_diff = target.ToDouble() / original.ToDouble();
|
||||
int64_t new_length = reader->info.video_length * rate_diff;
|
||||
|
||||
// Build curve for framerate mapping
|
||||
Keyframe rate_curve;
|
||||
rate_curve.AddPoint(1, 1, LINEAR);
|
||||
rate_curve.AddPoint(new_length, reader->info.video_length, LINEAR);
|
||||
// Calculate the value difference
|
||||
double value_increment = (reader->info.video_length + 1) / (double) (new_length);
|
||||
|
||||
// Loop through curve, and build list of frames
|
||||
double original_frame_num = 1.0f;
|
||||
for (int64_t frame_num = 1; frame_num <= new_length; frame_num++)
|
||||
{
|
||||
// Add 2 fields per frame
|
||||
AddField(rate_curve.GetInt(frame_num));
|
||||
AddField(rate_curve.GetInt(frame_num));
|
||||
AddField(round(original_frame_num));
|
||||
AddField(round(original_frame_num));
|
||||
|
||||
// Increment original frame number
|
||||
original_frame_num += value_increment;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,6 +308,11 @@ void FrameMapper::Init()
|
||||
|
||||
MappedFrame FrameMapper::GetMappedFrame(int64_t TargetFrameNumber)
|
||||
{
|
||||
// Check if mappings are dirty (and need to be recalculated)
|
||||
if (is_dirty)
|
||||
// Recalculate mappings
|
||||
Init();
|
||||
|
||||
// Ignore mapping on single image readers
|
||||
if (info.has_video and !info.has_audio and info.has_single_image) {
|
||||
// Return the same number
|
||||
@@ -743,14 +746,16 @@ void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldow
|
||||
SWR_FREE(&avr);
|
||||
avr = NULL;
|
||||
}
|
||||
|
||||
// Re-init mapping
|
||||
Init();
|
||||
}
|
||||
|
||||
// Resample audio and map channels (if needed)
|
||||
void FrameMapper::ResampleMappedAudio(std::shared_ptr<Frame> frame, int64_t original_frame_number)
|
||||
{
|
||||
// Check if mappings are dirty (and need to be recalculated)
|
||||
if (is_dirty)
|
||||
// Recalculate mappings
|
||||
Init();
|
||||
|
||||
// Init audio buffers / variables
|
||||
int total_frame_samples = 0;
|
||||
int channels_in_frame = frame->GetAudioChannelsCount();
|
||||
|
||||
213
src/KeyFrame.cpp
213
src/KeyFrame.cpp
@@ -296,17 +296,48 @@ bool Keyframe::IsIncreasing(int index)
|
||||
Process();
|
||||
|
||||
// Is index a valid point?
|
||||
if (index >= 0 && index < Values.size())
|
||||
// Return value
|
||||
return long(round(Values[index].IsIncreasing()));
|
||||
else if (index < 0 && Values.size() > 0)
|
||||
// Return the minimum value
|
||||
return long(round(Values[0].IsIncreasing()));
|
||||
else if (index >= Values.size() && Values.size() > 0)
|
||||
// return the maximum value
|
||||
return long(round(Values[Values.size() - 1].IsIncreasing()));
|
||||
if (index >= 1 && (index + 1) < Values.size()) {
|
||||
int64_t current_value = GetLong(index);
|
||||
int64_t previous_value = 0;
|
||||
int64_t next_value = 0;
|
||||
int64_t previous_repeats = 0;
|
||||
int64_t next_repeats = 0;
|
||||
|
||||
// Loop backwards and look for the next unique value
|
||||
for (vector<Coordinate>::iterator backwards_it = Values.begin() + index; backwards_it != Values.begin(); backwards_it--) {
|
||||
previous_value = long(round((*backwards_it).Y));
|
||||
if (previous_value == current_value) {
|
||||
// Found same value
|
||||
previous_repeats++;
|
||||
} else {
|
||||
// Found non repeating value, no more repeats found
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Loop forwards and look for the next unique value
|
||||
for (vector<Coordinate>::iterator forwards_it = Values.begin() + (index + 1); forwards_it != Values.end(); forwards_it++) {
|
||||
next_value = long(round((*forwards_it).Y));
|
||||
if (next_value == current_value) {
|
||||
// Found same value
|
||||
next_repeats++;
|
||||
} else {
|
||||
// Found non repeating value, no more repeats found
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (current_value < next_value) {
|
||||
// Increasing
|
||||
return true;
|
||||
}
|
||||
else if (current_value >= next_value) {
|
||||
// Decreasing
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
// return the default direction of most curves (i.e. increasing is true)
|
||||
// return default true (since most curves increase)
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -385,6 +416,7 @@ void Keyframe::SetJsonValue(Json::Value root) {
|
||||
}
|
||||
|
||||
// Get the fraction that represents how many times this value is repeated in the curve
|
||||
// This is depreciated and will be removed soon.
|
||||
Fraction Keyframe::GetRepeatFraction(int64_t index)
|
||||
{
|
||||
// Check if it needs to be processed
|
||||
@@ -392,17 +424,42 @@ Fraction Keyframe::GetRepeatFraction(int64_t index)
|
||||
Process();
|
||||
|
||||
// Is index a valid point?
|
||||
if (index >= 0 && index < Values.size())
|
||||
// Return value
|
||||
return Values[index].Repeat();
|
||||
else if (index < 0 && Values.size() > 0)
|
||||
// Return the minimum value
|
||||
return Values[0].Repeat();
|
||||
else if (index >= Values.size() && Values.size() > 0)
|
||||
// return the maximum value
|
||||
return Values[Values.size() - 1].Repeat();
|
||||
if (index >= 1 && (index + 1) < Values.size()) {
|
||||
int64_t current_value = GetLong(index);
|
||||
int64_t previous_value = 0;
|
||||
int64_t next_value = 0;
|
||||
int64_t previous_repeats = 0;
|
||||
int64_t next_repeats = 0;
|
||||
|
||||
// Loop backwards and look for the next unique value
|
||||
for (vector<Coordinate>::iterator backwards_it = Values.begin() + index; backwards_it != Values.begin(); backwards_it--) {
|
||||
previous_value = long(round((*backwards_it).Y));
|
||||
if (previous_value == current_value) {
|
||||
// Found same value
|
||||
previous_repeats++;
|
||||
} else {
|
||||
// Found non repeating value, no more repeats found
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Loop forwards and look for the next unique value
|
||||
for (vector<Coordinate>::iterator forwards_it = Values.begin() + (index + 1); forwards_it != Values.end(); forwards_it++) {
|
||||
next_value = long(round((*forwards_it).Y));
|
||||
if (next_value == current_value) {
|
||||
// Found same value
|
||||
next_repeats++;
|
||||
} else {
|
||||
// Found non repeating value, no more repeats found
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t total_repeats = previous_repeats + next_repeats;
|
||||
return Fraction(previous_repeats, total_repeats);
|
||||
}
|
||||
else
|
||||
// return a blank coordinate (0,0)
|
||||
// return a blank coordinate
|
||||
return Fraction(1,1);
|
||||
}
|
||||
|
||||
@@ -414,17 +471,48 @@ double Keyframe::GetDelta(int64_t index)
|
||||
Process();
|
||||
|
||||
// Is index a valid point?
|
||||
if (index >= 0 && index < Values.size())
|
||||
// Return value
|
||||
return Values[index].Delta();
|
||||
else if (index < 0 && Values.size() > 0)
|
||||
// Return the minimum value
|
||||
return Values[0].Delta();
|
||||
else if (index >= Values.size() && Values.size() > 0)
|
||||
// return the maximum value
|
||||
return Values[Values.size() - 1].Delta();
|
||||
if (index >= 1 && (index + 1) < Values.size()) {
|
||||
int64_t current_value = GetLong(index);
|
||||
int64_t previous_value = 0;
|
||||
int64_t next_value = 0;
|
||||
int64_t previous_repeats = 0;
|
||||
int64_t next_repeats = 0;
|
||||
|
||||
// Loop backwards and look for the next unique value
|
||||
for (vector<Coordinate>::iterator backwards_it = Values.begin() + index; backwards_it != Values.begin(); backwards_it--) {
|
||||
previous_value = long(round((*backwards_it).Y));
|
||||
if (previous_value == current_value) {
|
||||
// Found same value
|
||||
previous_repeats++;
|
||||
} else {
|
||||
// Found non repeating value, no more repeats found
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Loop forwards and look for the next unique value
|
||||
for (vector<Coordinate>::iterator forwards_it = Values.begin() + (index + 1); forwards_it != Values.end(); forwards_it++) {
|
||||
next_value = long(round((*forwards_it).Y));
|
||||
if (next_value == current_value) {
|
||||
// Found same value
|
||||
next_repeats++;
|
||||
} else {
|
||||
// Found non repeating value, no more repeats found
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for matching previous value (special case for 1st element)
|
||||
if (current_value == previous_value)
|
||||
previous_value = 0;
|
||||
|
||||
if (previous_repeats == 1)
|
||||
return current_value - previous_value;
|
||||
else
|
||||
return 0.0;
|
||||
}
|
||||
else
|
||||
// return a blank coordinate (0,0)
|
||||
// return a blank coordinate
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
@@ -529,7 +617,7 @@ void Keyframe::PrintValues() {
|
||||
|
||||
for (vector<Coordinate>::iterator it = Values.begin() + 1; it != Values.end(); it++) {
|
||||
Coordinate c = *it;
|
||||
cout << long(round(c.X)) << "\t" << c.Y << "\t" << c.IsIncreasing() << "\t" << c.Repeat().num << "\t" << c.Repeat().den << "\t" << c.Delta() << endl;
|
||||
cout << long(round(c.X)) << "\t" << c.Y << "\t" << IsIncreasing(c.X) << "\t" << GetRepeatFraction(c.X).num << "\t" << GetRepeatFraction(c.X).den << "\t" << GetDelta(c.X) << endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -567,69 +655,6 @@ void Keyframe::Process() {
|
||||
// process segment p1,p2
|
||||
ProcessSegment(x, p1, p2);
|
||||
}
|
||||
|
||||
// Loop through each Value, and set the direction of the coordinate. This is used
|
||||
// when time mapping, to determine what direction the audio waveforms play.
|
||||
bool increasing = true;
|
||||
int repeat_count = 1;
|
||||
int64_t last_value = 0;
|
||||
for (vector<Coordinate>::iterator it = Values.begin() + 1; it != Values.end(); it++) {
|
||||
int current_value = long(round((*it).Y));
|
||||
int64_t next_value = long(round((*it).Y));
|
||||
int64_t prev_value = long(round((*it).Y));
|
||||
if (it + 1 != Values.end())
|
||||
next_value = long(round((*(it + 1)).Y));
|
||||
if (it - 1 >= Values.begin())
|
||||
prev_value = long(round((*(it - 1)).Y));
|
||||
|
||||
// Loop forward and look for the next unique value (to determine direction)
|
||||
for (vector<Coordinate>::iterator direction_it = it + 1; direction_it != Values.end(); direction_it++) {
|
||||
int64_t next = long(round((*direction_it).Y));
|
||||
|
||||
// Detect direction
|
||||
if (current_value < next)
|
||||
{
|
||||
increasing = true;
|
||||
break;
|
||||
}
|
||||
else if (current_value > next)
|
||||
{
|
||||
increasing = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set direction
|
||||
(*it).IsIncreasing(increasing);
|
||||
|
||||
// Detect repeated Y value
|
||||
if (current_value == last_value)
|
||||
// repeated, so increment count
|
||||
repeat_count++;
|
||||
else
|
||||
// reset repeat counter
|
||||
repeat_count = 1;
|
||||
|
||||
// Detect how many 'more' times it's repeated
|
||||
int additional_repeats = 0;
|
||||
for (vector<Coordinate>::iterator repeat_it = it + 1; repeat_it != Values.end(); repeat_it++) {
|
||||
int64_t next = long(round((*repeat_it).Y));
|
||||
if (next == current_value)
|
||||
// repeated, so increment count
|
||||
additional_repeats++;
|
||||
else
|
||||
break; // stop looping
|
||||
}
|
||||
|
||||
// Set repeat fraction
|
||||
(*it).Repeat(Fraction(repeat_count, repeat_count + additional_repeats));
|
||||
|
||||
// Set delta (i.e. different from previous unique Y value)
|
||||
(*it).Delta(current_value - last_value);
|
||||
|
||||
// track the last value
|
||||
last_value = current_value;
|
||||
}
|
||||
}
|
||||
|
||||
// reset flag
|
||||
|
||||
@@ -377,4 +377,20 @@ TEST(Keyframe_Remove_Duplicate_Point)
|
||||
// Spot check values from the curve
|
||||
CHECK_EQUAL(kf.GetLength(), 1);
|
||||
CHECK_CLOSE(kf.GetPoint(0).co.Y, 2.0, 0.01);
|
||||
}
|
||||
|
||||
TEST(Keyframe_Large_Number_Values)
|
||||
{
|
||||
// Large value
|
||||
int64_t large_value = 30 * 60 * 90;
|
||||
|
||||
// Create a keyframe curve with 2 points
|
||||
Keyframe kf;
|
||||
kf.AddPoint(1, 1.0);
|
||||
kf.AddPoint(large_value, 100.0); // 90 minutes long
|
||||
|
||||
// Spot check values from the curve
|
||||
CHECK_EQUAL(kf.GetLength(), large_value + 1);
|
||||
CHECK_CLOSE(kf.GetPoint(0).co.Y, 1.0, 0.01);
|
||||
CHECK_CLOSE(kf.GetPoint(1).co.Y, 100.0, 0.01);
|
||||
}
|
||||
Reference in New Issue
Block a user