From f1367ba2fdf4eb820e3db04eb113822780064e4d Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Sun, 10 Apr 2011 16:02:55 +0100 Subject: [PATCH] Add a new BezierCurve::uniform function which has linear arc length behaviour This improves the animation of train moving along curves. --- include/BezierCurve.hpp | 34 ++++++++++++++++++++++++++++++---- src/GenTrack.cpp | 12 +++++++----- src/Points.cpp | 12 +++++++----- src/SlopeTrack.cpp | 10 ++++++---- 4 files changed, 50 insertions(+), 18 deletions(-) diff --git a/include/BezierCurve.hpp b/include/BezierCurve.hpp index c9593b2..ea4e29d 100644 --- a/include/BezierCurve.hpp +++ b/include/BezierCurve.hpp @@ -38,13 +38,11 @@ struct BezierCurve { Vector cur = operator()(0.0), prev; length = 0.0; - - const T step = static_cast(0.01); - const T min = static_cast(0.1); + const T step = static_cast(0.0001); const T max = static_cast(1.0); - for (T t = min; t <= max; t += step) { + for (T t = step; t <= max; t += step) { prev = cur; cur = operator()(t); @@ -83,6 +81,34 @@ struct BezierCurve { ); } + // A slower approximation to the curve function that guarantees + // uniform(k).length() = curve_length * k + Vector uniform(T s, T *out = NULL) const + { + Vector cur = operator()(0.0), prev; + + T now = static_cast(0.0); + const T target = length * s; + + const T step = static_cast(0.0001); + const T max = static_cast(1.0); + + // TODO: could speed this up using a lookup table + T t; + for (t = step; t <= max && now < target; t += step) { + prev = cur; + cur = operator()(t); + + const Vector diff = cur - prev; + now += sqrt(diff.x*diff.x + diff.y*diff.y + diff.z*diff.z); + } + + if (out) + *out = std::max(static_cast(0.0), + std::min(t, static_cast(1.0))); + return cur; + } + // The derivative with respect to t at a point Vector deriv(T t) const { diff --git a/src/GenTrack.cpp b/src/GenTrack.cpp index db13020..06acc34 100644 --- a/src/GenTrack.cpp +++ b/src/GenTrack.cpp @@ -165,10 +165,11 @@ void GenTrack::merge(IMeshBufferPtr buf) const for (int i = 0; i <= n; i++) { float pos = (sleeper_sep / 2) + i * (sleeper_sep + delta); - - Vector v = curve(pos / curve.length); - const Vector deriv = curve.deriv(pos / curve.length); + float u_curve_delta; + Vector v = curve.uniform(pos / curve.length, &u_curve_delta); + + const Vector deriv = curve.deriv(u_curve_delta); const float angle = rad_to_deg(atanf(deriv.z / deriv.x)); @@ -347,14 +348,15 @@ void GenTrack::transform(const track::TravelToken& token, const float curve_delta = (backwards ? curve.length - delta : delta) / curve.length; - Vector curve_value = curve(curve_delta); + float u_curve_delta; + Vector curve_value = curve.uniform(curve_delta, &u_curve_delta); glTranslatef( static_cast(origin.x) + curve_value.x, height, static_cast(origin.y) + curve_value.z); - float angle = rotation_at(curve_delta); + float angle = rotation_at(u_curve_delta); if (backwards) angle += 180.0f; diff --git a/src/Points.cpp b/src/Points.cpp index 947e01d..189b713 100644 --- a/src/Points.cpp +++ b/src/Points.cpp @@ -25,7 +25,6 @@ #include -#include #include // Forks in the track @@ -218,12 +217,14 @@ void Points::merge(IMeshBufferPtr buf) const // Draw the curved sleepers for (float i = 0.25f; i < 1.0f; i += 0.08f) { - const VectorF v = (reflected ? my_reflected_curve : my_curve)(i); + float u_i; + const BezierCurve& c = reflected ? my_reflected_curve : my_curve; + const VectorF v = c.uniform(i, &u_i); const VectorF t = make_vector(v.x - 0.5f, 0.0f, v.z); const VectorF soff = off + rotateY(t, y_angle); const VectorF deriv = - (reflected ? my_reflected_curve : my_curve).deriv(i); + (reflected ? my_reflected_curve : my_curve).deriv(u_i); const float angle = rad_to_deg(atanf(deriv.z / deriv.x)); @@ -355,11 +356,12 @@ void Points::transform(const track::TravelToken& a_token, float delta) const bool backwards = a_token.position == displaced_endpoint(); const float f_value = backwards ? 1.0f - curve_delta : curve_delta; - const VectorF curve_value = my_curve(f_value); + float u_f_value; + const VectorF curve_value = my_curve.uniform(f_value, &u_f_value); // Calculate the angle that the tangent to the curve at this // point makes to (one of) the axis at this point - const VectorF deriv = my_curve.deriv(f_value); + const VectorF deriv = my_curve.deriv(u_f_value); const float angle = rad_to_deg(atanf(deriv.z / deriv.x)); diff --git a/src/SlopeTrack.cpp b/src/SlopeTrack.cpp index f57f492..3c3597c 100644 --- a/src/SlopeTrack.cpp +++ b/src/SlopeTrack.cpp @@ -122,11 +122,12 @@ void SlopeTrack::merge(IMeshBufferPtr buf) const // Draw the sleepers for (float t = 0.1f; t < 1.0f; t += 0.25f) { - const Vector curve_value = curve(t); + float u_curve_value; + const Vector curve_value = curve.uniform(t, &u_curve_value); #if 0 // Should the sleepers be at the same angle as the slope? - const Vector deriv = curve.deriv(t); + const Vector deriv = curve.deriv(u_curve_value); const float angle = rad_to_deg(atanf(deriv.y / deriv.x)); #endif @@ -229,7 +230,8 @@ void SlopeTrack::transform(const track::TravelToken& token, float delta) const const float curve_delta = delta / length; - const Vector curve_value = curve(curve_delta); + float u_curve_delta; + const Vector curve_value = curve.uniform(curve_delta, &u_curve_delta); const float x_trans = axis == axis::X ? curve_value.x : 0.0f; const float y_trans =curve_value.y; @@ -247,7 +249,7 @@ void SlopeTrack::transform(const track::TravelToken& token, float delta) const if (token.direction == -axis) glRotated(-180.0, 0.0, 1.0, 0.0); - const Vector deriv = curve.deriv(curve_delta); + const Vector deriv = curve.deriv(u_curve_delta); const float angle = rad_to_deg(atanf(deriv.y / deriv.x)); -- 2.39.2