<commit_before>// Copyright 2019 Google Inc. All rights reserved.
//
// 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 "motive/util/keyframe_converter.h"

#include <vector>

namespace motive {

namespace {

/// Converts |time| to Motive's millisecond time unit using |ms_per_time_unit|
/// as the conversion factor.
float ConvertToMilliseconds(float time, float ms_per_time_unit) {
  return time * ms_per_time_unit;
}

/// Determines the index of a float* representing a quaternion to access for
/// a particular |type| and |order|.
int IndexForQuaternionComponent(MatrixOperationType type,
                                QuaternionOrder order) {
  if (order == kOrderWXYZ) {
    return static_cast<int>(type - kQuaternionW);
  } else if (type == kQuaternionW) {
    return 3;
  } else {
    return static_cast<int>(type - kQuaternionX);
  }
}

void AddLinearKeyframeData(std::vector<UncompressedNode>* nodes_per_channel,
                           size_t channel_count, const KeyframeData& data) {
  // Resize the lists of nodes to the proper size. We can "fake" linear
  // interpolation by having two spline nodes at every keyframe: the first uses
  // the left tangent as it's derivative and the second uses the right tangent.
  const size_t num_nodes = data.count * 2;
  for (int i = 0; i < channel_count; ++i) {
    nodes_per_channel[i].resize(num_nodes);
  }

  // Traverse the data in sample order and populate the nodes for each channel.
  for (int i = 0; i < data.count; ++i) {
    // The index into |data.values| of the first value of the current keyframe.
    const int keyframe = i * channel_count;
    const float time =
        ConvertToMilliseconds(data.times[i], data.ms_per_time_unit);
    for (int j = 0; j < channel_count; ++j) {
      const int left_node_index = i * 2;
      const float value = data.values[keyframe + j];

      // Populate the left and right nodes with the same times and values.
      auto& left_node = nodes_per_channel[j][left_node_index];
      left_node.x = time;
      left_node.y = value;

      auto& right_node = nodes_per_channel[j][left_node_index + 1];
      right_node.x = time;
      right_node.y = value;

      // Compute this left node's derivative and the previous right node's
      // derivative as the tangent line between the two.
      if (left_node_index == 0) {
        // The first left node has a zero left derivative.
        left_node.derivative = 0.f;
      } else {
        auto& prev_right_node = nodes_per_channel[j][left_node_index - 1];
        const float tangent =
            (value - prev_right_node.y) / (time - prev_right_node.x);
        prev_right_node.derivative = tangent;
        left_node.derivative = tangent;

        // The last right node has a zero right derivative.
        if (i == data.count - 1) {
          right_node.derivative = 0.f;
        }
      }
    }
  }
}

void AddStepKeyframeData(std::vector<UncompressedNode>* nodes_per_channel,
                         size_t channel_count,
                         const KeyframeData& data) {
  // Resize the lists of nodes to the proper size. We can "fake" step
  // interpolation by having two spline nodes at every keyframe: the first uses
  // the value of the previous keyframe and the second uses the current.
  const size_t num_nodes = data.count * 2;
  for (int i = 0; i < channel_count; ++i) {
    nodes_per_channel[i].resize(num_nodes);
  }
<commit_msg>Avoid a divide-by-zero error in linear keyframe conversion.<commit_after>// Copyright 2019 Google Inc. All rights reserved.
//
// 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 "motive/util/keyframe_converter.h"

#include <vector>

namespace motive {

namespace {

/// Converts |time| to Motive's millisecond time unit using |ms_per_time_unit|
/// as the conversion factor.
float ConvertToMilliseconds(float time, float ms_per_time_unit) {
  return time * ms_per_time_unit;
}

/// Determines the index of a float* representing a quaternion to access for
/// a particular |type| and |order|.
int IndexForQuaternionComponent(MatrixOperationType type,
                                QuaternionOrder order) {
  if (order == kOrderWXYZ) {
    return static_cast<int>(type - kQuaternionW);
  } else if (type == kQuaternionW) {
    return 3;
  } else {
    return static_cast<int>(type - kQuaternionX);
  }
}

void AddLinearKeyframeData(std::vector<UncompressedNode>* nodes_per_channel,
                           size_t channel_count, const KeyframeData& data) {
  // Resize the lists of nodes to the proper size. We can "fake" linear
  // interpolation by having two spline nodes at every keyframe: the first uses
  // the left tangent as it's derivative and the second uses the right tangent.
  const size_t num_nodes = data.count * 2;
  for (int i = 0; i < channel_count; ++i) {
    nodes_per_channel[i].resize(num_nodes);
  }

  // Traverse the data in sample order and populate the nodes for each channel.
  for (int i = 0; i < data.count; ++i) {
    // The index into |data.values| of the first value of the current keyframe.
    const int keyframe = i * channel_count;
    const float time =
        ConvertToMilliseconds(data.times[i], data.ms_per_time_unit);
    for (int j = 0; j < channel_count; ++j) {
      const int left_node_index = i * 2;
      const float value = data.values[keyframe + j];

      // Populate the left and right nodes with the same times and values.
      auto& left_node = nodes_per_channel[j][left_node_index];
      left_node.x = time;
      left_node.y = value;

      auto& right_node = nodes_per_channel[j][left_node_index + 1];
      right_node.x = time;
      right_node.y = value;

      // Compute this left node's derivative and the previous right node's
      // derivative as the tangent line between the two.
      if (left_node_index == 0) {
        // The first left node has a zero left derivative.
        left_node.derivative = 0.f;
      } else {
        auto& prev_right_node = nodes_per_channel[j][left_node_index - 1];
        const float delta_time = time - prev_right_node.x;
        const float tangent =
            delta_time > 0.f ? (value - prev_right_node.y) / delta_time : 0.f;
        prev_right_node.derivative = tangent;
        left_node.derivative = tangent;

        // The last right node has a zero right derivative.
        if (i == data.count - 1) {
          right_node.derivative = 0.f;
        }
      }
    }
  }
}

void AddStepKeyframeData(std::vector<UncompressedNode>* nodes_per_channel,
                         size_t channel_count, const KeyframeData& data) {
  // Resize the lists of nodes to the proper size. We can "fake" step
  // interpolation by having two spline nodes at every keyframe: the first uses
  // the value of the previous keyframe and the second uses the current.
  const size_t num_nodes = data.count * 2;
  for (int i = 0; i < channel_count; ++i) {
    nodes_per_channel[i].resize(num_nodes);
  }

{'fraction_non_alphanumeric': 0.09119536465549817, 'fraction_numerical': 0.005794180627283033, 'mean_word_length': 2.8265060240963855, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 14, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6873437', 'n_tokens_mistral': 2422, 'n_tokens_neox': 2324, 'n_words': 850}