int main() { //! [Own Processor Register] LinearInit::Register(); //! [Own Processor Register] MotiveEngine engine; //! [Own Processor Create Instance] // Move from 10 --> -5 in 100 internal time units. const MotiveTarget1f target = motive::CurrentToTarget1f(10, 0, -5, 0, 100); // Create the one dimensional motivator of type Linear by passing in // LinearInit. Motivator1f linear_motivator(LinearInit(), &engine, target); //! [Own Processor Create Instance] //! [Own Processor Advance Simulation] // Advance the simulation one tick at a time by calling engine.AdvanceFrame(). std::vector<vec2> points; points.reserve(target.EndTime() + 1); for (MotiveTime t = 0; t <= target.EndTime(); ++t) { points.push_back(vec2(static_cast<float>(t), linear_motivator.Value())); engine.AdvanceFrame(1); } printf("\n%s", motive::Graph2DPoints(&points[0], static_cast<int>(points.size())) .c_str()); //! [Own Processor Advance Simulation] return 0; }
// TODO: Change to CreateSplineToTarget() void SetTarget(MotiveIndex index, const MotiveTarget1f& t) { SplineData& d = Data(index); // If the first node specifies time=0 or there is no valid data in the // interpolator, we want to override the current values with the values // specified in the first node. const MotiveNode1f& node0 = t.Node(0); const bool override_current = node0.time == 0 || !interpolator_.Valid(index); // TODO(b/65298927): It seems that the animation pipeline can produce data // that is out of range. Instead of just using node0.value directly, if // the interpolator is doing modular arithmetic, normalize the y value to // the modulator's range. const float start_y = override_current ? (interpolator_.ModularArithmetic(index) ? interpolator_.ModularRange(index).Normalize(node0.value) : node0.value) : interpolator_.NormalizedY(index); const float start_derivative = override_current ? node0.velocity : Velocity(index); const int start_node_index = override_current ? 1 : 0; // Ensure we have a local spline available, allocated from our pool of // splines. if (d.local_spline == nullptr) { d.local_spline = AllocateSpline(); } // Initialize the compact spline to hold the sequence of nodes in 't'. // Add the first node, which has the start condition. const float end_x = static_cast<float>(t.EndTime()); const Range y_range = CalculateYRange(index, t, start_y); const float x_granularity = CompactSpline::RecommendXGranularity(end_x); d.local_spline->Init(y_range, x_granularity); d.local_spline->AddNode(0.0f, start_y, start_derivative); // Add subsequent nodes, in turn, taking care to respect the 'direction' // request when using modular arithmetic. float prev_y = start_y; for (int i = start_node_index; i < t.num_nodes(); ++i) { const MotiveNode1f& n = t.Node(i); const float y = interpolator_.NextY(index, prev_y, n.value, n.direction); d.local_spline->AddNode(static_cast<float>(n.time), y, n.velocity, motive::kAddWithoutModification); prev_y = y; } // Point the interpolator at the spline we just created. Always start our // spline at time 0. interpolator_.SetSplines(index, 1, d.local_spline, SplinePlayback()); }
Range CalculateYRange(MotiveIndex index, const MotiveTarget1f& t, float start_y) const { if (interpolator_.ModularArithmetic(index)) { // For modular splines, we need to expand the spline's y-range to match // the number of nodes in the spline. It's possible for the spline to jump // up the entire range every node, so the range has to be broad enough // to hold it all. // // Note that we only normalize the first value of the spline, and // subsequent values are allowed to curve out of the normalized range. const float num_spline_nodes = static_cast<float>(t.num_nodes()); return interpolator_.ModularRange(index).Lengthen(num_spline_nodes); } // Calculate the union of the y ranges in the target, then expand it a // little to allow for intermediate nodes that jump slightly beyond the // union's range. return t.ValueRange(start_y).Lengthen(kYRangeBufferPercent); }
virtual void SetTarget(MotiveIndex index, const MotiveTarget1f& t) { OvershootData& d = Data(index); // A 'time' of 0 means that we're setting the current values. const MotiveNode1f& current = t.Node(0); if (current.time == 0) { d.value = current.value; d.velocity = current.velocity; } // A 'time' > 0 means that we're setting the target values. // We can also use the second node to set target values, if it exists. const MotiveNode1f* target = current.time == 0 ? (t.num_nodes() > 1 ? &t.Node(1) : nullptr) : &t.Node(0); if (target != nullptr) { d.target_value = target->value; } }