aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Freilich <sfreilich@google.com>2022-03-24 09:47:28 -0700
committerCopybara-Service <copybara-worker@google.com>2022-03-24 09:48:01 -0700
commit789fe66bb8fecd066288d6b7b4466da5d3026ded (patch)
tree2121159be508e1b0ca7b94bc015e8d5ca5a237a5
parent72b3529f326eead6d715ac5b0f52464bac716419 (diff)
downloadink-stroke-modeler-789fe66bb8fecd066288d6b7b4466da5d3026ded.tar.gz
Handle start==end correctly in Normalize
In particular, Clamp01(Normalize(x, x, y)) should be 1 if y > x and 0 if y < x. (The real ambiguous case is where start == end == value, in which case we're still having Clamp01(Normalize(x, x, x)) be 0 arbitrarily.) Instead, rename Normalize to Normalize01, clamp the results to the range [0, 1], and return 0 if value < start and 1 if value > end. This allows us to pick the correct (none or all) amount of interpolation in WobbleSmoother when WobbleSmootherParams.speed_floor == WobbleSmootherParams.speed_ceiling, which is permitted. PiperOrigin-RevId: 437017353
-rw-r--r--ink_stroke_modeler/internal/prediction/kalman_predictor.cc16
-rw-r--r--ink_stroke_modeler/internal/utils.h11
-rw-r--r--ink_stroke_modeler/internal/utils_test.cc13
-rw-r--r--ink_stroke_modeler/internal/wobble_smoother.cc2
-rw-r--r--ink_stroke_modeler/internal/wobble_smoother_test.cc23
5 files changed, 48 insertions, 17 deletions
diff --git a/ink_stroke_modeler/internal/prediction/kalman_predictor.cc b/ink_stroke_modeler/internal/prediction/kalman_predictor.cc
index ab24540..cc8949c 100644
--- a/ink_stroke_modeler/internal/prediction/kalman_predictor.cc
+++ b/ink_stroke_modeler/internal/prediction/kalman_predictor.cc
@@ -226,8 +226,8 @@ int KalmanPredictor::NumberOfPointsToPredict(
float estimated_error =
Distance(*last_position_received_, estimated_state.position);
float normalized_error =
- Clamp01(1.f - Normalize(0.f, confidence_params.max_estimation_distance,
- estimated_error));
+ 1.f - Normalize01(0.f, confidence_params.max_estimation_distance,
+ estimated_error);
// This is the state that the prediction would end at if we predicted the full
// interval (i.e. if confidence == 1).
@@ -240,8 +240,8 @@ int KalmanPredictor::NumberOfPointsToPredict(
Distance(estimated_state.position, end_state.position) /
static_cast<float>(predictor_params_.prediction_interval.Value());
float normalized_distance =
- Clamp01(Normalize(confidence_params.min_travel_speed,
- confidence_params.max_travel_speed, travel_speed));
+ Normalize01(confidence_params.min_travel_speed,
+ confidence_params.max_travel_speed, travel_speed);
// If the actual prediction differs too much from the linear prediction, it
// suggests that the acceleration and jerk components overtake the velocity,
@@ -251,10 +251,10 @@ int KalmanPredictor::NumberOfPointsToPredict(
estimated_state.position +
static_cast<float>(predictor_params_.prediction_interval.Value()) *
estimated_state.velocity);
- float linearity = Interp(
- confidence_params.baseline_linearity_confidence, 1.f,
- 1.f - Clamp01(Normalize(0.f, confidence_params.max_linear_deviation,
- deviation_from_linear_prediction)));
+ float linearity =
+ Interp(confidence_params.baseline_linearity_confidence, 1.f,
+ 1.f - Normalize01(0.f, confidence_params.max_linear_deviation,
+ deviation_from_linear_prediction));
auto confidence =
sample_ratio * normalized_error * normalized_distance * linearity;
diff --git a/ink_stroke_modeler/internal/utils.h b/ink_stroke_modeler/internal/utils.h
index bd648b7..6bf2a27 100644
--- a/ink_stroke_modeler/internal/utils.h
+++ b/ink_stroke_modeler/internal/utils.h
@@ -34,10 +34,13 @@ inline float Clamp01(float value) {
}
// Returns the ratio of the difference from `start` to `value` and the
-// difference from `start` to `end`.
-inline float Normalize(float start, float end, float value) {
- if (start == end) return 0;
- return (value - start) / (end - start);
+// difference from `start` to `end`, clamped to the range [0, 1]. If
+// `start` == `end`, returns 1 if `value` > `start`, 0 otherwise.
+inline float Normalize01(float start, float end, float value) {
+ if (start == end) {
+ return value > start ? 1 : 0;
+ }
+ return Clamp01((value - start) / (end - start));
}
// Linearly interpolates between `start` and `end`, clamping the interpolation
diff --git a/ink_stroke_modeler/internal/utils_test.cc b/ink_stroke_modeler/internal/utils_test.cc
index 426cc27..b50d21a 100644
--- a/ink_stroke_modeler/internal/utils_test.cc
+++ b/ink_stroke_modeler/internal/utils_test.cc
@@ -14,6 +14,8 @@
#include "ink_stroke_modeler/internal/utils.h"
+#include <cmath>
+
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "ink_stroke_modeler/internal/type_matchers.h"
@@ -31,10 +33,13 @@ TEST(UtilsTest, Clamp01) {
EXPECT_FLOAT_EQ(Clamp01(1.1), 1);
}
-TEST(UtilsTest, Normalize) {
- EXPECT_FLOAT_EQ(Normalize(1, 2, 1.5), .5);
- EXPECT_FLOAT_EQ(Normalize(7, 3, 4), .75);
- EXPECT_FLOAT_EQ(Normalize(-1, 1, 2), 1.5);
+TEST(UtilsTest, Normalize01) {
+ EXPECT_FLOAT_EQ(Normalize01(1, 2, 1.5), .5);
+ EXPECT_FLOAT_EQ(Normalize01(7, 3, 4), .75);
+ EXPECT_FLOAT_EQ(Normalize01(-1, 1, 2), 1);
+ EXPECT_FLOAT_EQ(Normalize01(1, 1, 1), 0);
+ EXPECT_FLOAT_EQ(Normalize01(1, 1, 0), 0);
+ EXPECT_FLOAT_EQ(Normalize01(1, 1, 2), 1);
}
TEST(UtilsTest, InterpFloat) {
diff --git a/ink_stroke_modeler/internal/wobble_smoother.cc b/ink_stroke_modeler/internal/wobble_smoother.cc
index 96fe90d..a403ecb 100644
--- a/ink_stroke_modeler/internal/wobble_smoother.cc
+++ b/ink_stroke_modeler/internal/wobble_smoother.cc
@@ -63,7 +63,7 @@ Vec2 WobbleSmoother::Update(Vec2 position, Time time) {
float avg_speed = speed_sum_ / samples_.size();
return Interp(
avg_position, position,
- Normalize(params_.speed_floor, params_.speed_ceiling, avg_speed));
+ Normalize01(params_.speed_floor, params_.speed_ceiling, avg_speed));
}
} // namespace stroke_model
diff --git a/ink_stroke_modeler/internal/wobble_smoother_test.cc b/ink_stroke_modeler/internal/wobble_smoother_test.cc
index f31ac9d..7bffd37 100644
--- a/ink_stroke_modeler/internal/wobble_smoother_test.cc
+++ b/ink_stroke_modeler/internal/wobble_smoother_test.cc
@@ -37,6 +37,18 @@ TEST(WobbleSmootherTest, SlowStraightLine) {
EXPECT_THAT(filter.Update({3.064, 4}, Time{1.064}), Vec2Eq({3.048, 4}));
}
+TEST(WobbleSmootherTest, SlowStraightLineEqualFloorAndCeiling) {
+ // The line moves at 1 cm/s, which is below the floor of 1.31 cm/s.
+ WobbleSmootherParams equal_floor_and_ceiling_params{
+ .timeout = Duration(.04), .speed_floor = 1.31, .speed_ceiling = 1.31};
+ WobbleSmoother filter;
+ filter.Reset(equal_floor_and_ceiling_params, {3, 4}, Time{1});
+ EXPECT_THAT(filter.Update({3.016, 4}, Time{1.016}), Vec2Eq({3.008, 4}));
+ EXPECT_THAT(filter.Update({3.032, 4}, Time{1.032}), Vec2Eq({3.016, 4}));
+ EXPECT_THAT(filter.Update({3.048, 4}, Time{1.048}), Vec2Eq({3.032, 4}));
+ EXPECT_THAT(filter.Update({3.064, 4}, Time{1.064}), Vec2Eq({3.048, 4}));
+}
+
TEST(WobbleSmootherTest, FastStraightLine) {
// The line moves at 1.5 cm/s, which is above the ceiling of 1.44 cm/s.
WobbleSmoother filter;
@@ -46,6 +58,17 @@ TEST(WobbleSmootherTest, FastStraightLine) {
EXPECT_THAT(filter.Update({-1, .072}, Time{.048}), Vec2Eq({-1, .072}));
}
+TEST(WobbleSmootherTest, FastStraightLineEqualFloorAndCeiling) {
+ // The line moves at 1.5 cm/s, which is above the ceiling of 1.44 cm/s.
+ WobbleSmoother filter;
+ WobbleSmootherParams equal_floor_and_ceiling_params{
+ .timeout = Duration(.04), .speed_floor = 1.41, .speed_ceiling = 1.41};
+ filter.Reset(equal_floor_and_ceiling_params, {-1, 0}, Time{0});
+ EXPECT_THAT(filter.Update({-1, .024}, Time{.016}), Vec2Eq({-1, .024}));
+ EXPECT_THAT(filter.Update({-1, .048}, Time{.032}), Vec2Eq({-1, .048}));
+ EXPECT_THAT(filter.Update({-1, .072}, Time{.048}), Vec2Eq({-1, .072}));
+}
+
TEST(WobbleSmootherTest, SlowZigZag) {
// The line moves at 1 cm/s, which is below the floor of 1.31 cm/s.
WobbleSmoother filter;