aboutsummaryrefslogtreecommitdiff
path: root/ink_stroke_modeler/internal/position_modeler_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'ink_stroke_modeler/internal/position_modeler_test.cc')
-rw-r--r--ink_stroke_modeler/internal/position_modeler_test.cc317
1 files changed, 317 insertions, 0 deletions
diff --git a/ink_stroke_modeler/internal/position_modeler_test.cc b/ink_stroke_modeler/internal/position_modeler_test.cc
new file mode 100644
index 0000000..7898ea1
--- /dev/null
+++ b/ink_stroke_modeler/internal/position_modeler_test.cc
@@ -0,0 +1,317 @@
+// Copyright 2022 Google LLC
+//
+// 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 "ink_stroke_modeler/internal/position_modeler.h"
+
+#include <cmath>
+#include <iterator>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "ink_stroke_modeler/internal/internal_types.h"
+#include "ink_stroke_modeler/internal/type_matchers.h"
+#include "ink_stroke_modeler/params.h"
+#include "ink_stroke_modeler/types.h"
+
+namespace ink {
+namespace stroke_model {
+namespace {
+
+using ::testing::ElementsAre;
+
+const Duration kDefaultTimeStep(1. / 180);
+constexpr float kTol = .00005;
+
+// The expected position values are taken directly from results the old
+// TipDynamics class. The expected velocity values are from the same source, but
+// a multiplier of 300 (i.e. dt / (1 - drag)) had to be applied to account for
+// the fact that PositionModeler uses the time step correctly.
+
+TEST(PositionModelerTest, StraightLine) {
+ PositionModeler modeler;
+ Time current_time(0);
+ modeler.Reset({{0, 0}, {0, 0}, current_time}, PositionModelerParams());
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(modeler.Update({1, 0}, current_time),
+ TipStateNear({{.0909, 0}, {16.3636, 0}, current_time}, kTol));
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(modeler.Update({2, 0}, current_time),
+ TipStateNear({{.319, 0}, {41.0579, 0}, current_time}, kTol));
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(modeler.Update({3, 0}, current_time),
+ TipStateNear({{.6996, 0}, {68.5055, 0}, current_time}, kTol));
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(modeler.Update({4, 0}, current_time),
+ TipStateNear({{1.228, 0}, {95.1099, 0}, current_time}, kTol));
+}
+
+TEST(PositionModelerTest, ZigZag) {
+ PositionModeler modeler;
+ Time current_time(3);
+ modeler.Reset({{-1, -1}, {0, 0}, current_time}, PositionModelerParams());
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(modeler.Update({-.5, -1}, current_time),
+ TipStateNear({{-.9545, -1}, {8.1818, 0}, current_time}, kTol));
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(
+ modeler.Update({-.5, -.5}, current_time),
+ TipStateNear({{-.886, -.9545}, {12.3471, 8.1818}, current_time}, kTol));
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(
+ modeler.Update({0, -.5}, current_time),
+ TipStateNear({{-.7643, -.886}, {21.9056, 12.3471}, current_time}, kTol));
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(
+ modeler.Update({0, 0}, current_time),
+ TipStateNear({{-.6218, -.7643}, {25.6493, 21.9056}, current_time}, kTol));
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(
+ modeler.Update({.5, 0}, current_time),
+ TipStateNear({{-.4343, -.6218}, {33.7456, 25.6493}, current_time}, kTol));
+}
+
+TEST(PositionModelerTest, SharpTurn) {
+ PositionModeler modeler;
+ Time current_time(1.6);
+ modeler.Reset({{0, 0}, {0, 0}, current_time}, PositionModelerParams());
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(
+ modeler.Update({.25, .25}, current_time),
+ TipStateNear({{.0227, .0227}, {4.0909, 4.0909}, current_time}, kTol));
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(
+ modeler.Update({.5, .5}, current_time),
+ TipStateNear({{.0798, .0798}, {10.2645, 10.2645}, current_time}, kTol));
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(
+ modeler.Update({.75, .75}, current_time),
+ TipStateNear({{.1749, .1749}, {17.1264, 17.1264}, current_time}, kTol));
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(
+ modeler.Update({1, 1}, current_time),
+ TipStateNear({{.307, .307}, {23.7775, 23.7775}, current_time}, kTol));
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(
+ modeler.Update({1.25, .75}, current_time),
+ TipStateNear({{.472, .4265}, {29.6975, 21.5157}, current_time}, kTol));
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(
+ modeler.Update({1.5, .5}, current_time),
+ TipStateNear({{.6644, .5049}, {34.6406, 14.1117}, current_time}, kTol));
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(
+ modeler.Update({1.75, .25}, current_time),
+ TipStateNear({{.8786, .5288}, {38.5482, 4.2955}, current_time}, kTol));
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(
+ modeler.Update({2, 0}, current_time),
+ TipStateNear({{1.109, .495}, {41.4794, -6.0756}, current_time}, kTol));
+}
+
+TEST(PositionModelerTest, SmoothTurn) {
+ auto point_on_circle = [](float theta) {
+ return Vec2{std::cos(theta), std::sin(theta)};
+ };
+
+ PositionModeler modeler;
+ Time current_time(10.1);
+ modeler.Reset({point_on_circle(0), {0, 0}, current_time},
+ PositionModelerParams());
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(
+ modeler.Update(point_on_circle(M_PI * .125), current_time),
+ TipStateNear({{.9931, .0348}, {-1.2456, 6.2621}, current_time}, kTol));
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(
+ modeler.Update(point_on_circle(M_PI * .25), current_time),
+ TipStateNear({{0.9629, 0.1168}, {-5.4269, 14.7588}, current_time}, kTol));
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(modeler.Update(point_on_circle(M_PI * .375), current_time),
+ TipStateNear(
+ {{0.8921, 0.2394}, {-12.7511, 22.0623}, current_time}, kTol));
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(modeler.Update(point_on_circle(M_PI * .5), current_time),
+ TipStateNear(
+ {{0.7685, 0.3820}, {-22.2485, 25.6844}, current_time}, kTol));
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(modeler.Update(point_on_circle(M_PI * .625), current_time),
+ TipStateNear(
+ {{0.5897, 0.5169}, {-32.1865, 24.2771}, current_time}, kTol));
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(modeler.Update(point_on_circle(M_PI * .75), current_time),
+ TipStateNear(
+ {{0.3645, 0.6151}, {-40.5319, 17.6785}, current_time}, kTol));
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(
+ modeler.Update(point_on_circle(M_PI * .875), current_time),
+ TipStateNear({{0.1123, 0.6529}, {-45.4017, 6.8034}, current_time}, kTol));
+
+ current_time += kDefaultTimeStep;
+ EXPECT_THAT(
+ modeler.Update(point_on_circle(M_PI), current_time),
+ TipStateNear({{-0.1402, 0.6162}, {-45.4417, -6.6022}, current_time},
+ kTol));
+}
+
+TEST(PositionModelerTest, UpdateAlongLinearPath) {
+ PositionModeler modeler;
+ modeler.Reset({{5, 10}, {0, 0}, Time{3}}, PositionModelerParams());
+
+ std::vector<TipState> result;
+ modeler.UpdateAlongLinearPath({5, 10}, Time{3}, {15, 10}, Time{3.05}, 5,
+ std::back_inserter(result));
+ EXPECT_THAT(
+ result,
+ ElementsAre(
+ TipStateNear({{5.5891, 10}, {58.9091, 0}, Time{3.01}}, kTol),
+ TipStateNear({{6.7587, 10}, {116.9613, 0}, Time{3.02}}, kTol),
+ TipStateNear({{8.3355, 10}, {157.6746, 0}, Time{3.03}}, kTol),
+ TipStateNear({{10.1509, 10}, {181.5411, 0}, Time{3.04}}, kTol),
+ TipStateNear({{12.0875, 10}, {193.6607, 0}, Time{3.05}}, kTol)));
+
+ result.clear();
+ modeler.UpdateAlongLinearPath({15, 10}, Time{3.05}, {15, 16}, Time{3.08}, 3,
+ std::back_inserter(result));
+ EXPECT_THAT(
+ result,
+ ElementsAre(
+ TipStateNear({{13.4876, 10.5891}, {140.0123, 58.9091}, Time{3.06}},
+ kTol),
+ TipStateNear({{14.3251, 11.7587}, {83.7508, 116.9613}, Time{3.07}},
+ kTol),
+ TipStateNear({{14.7584, 13.3355}, {43.3291, 157.6746}, Time{3.08}},
+ kTol)));
+}
+
+TEST(PositionModelerTest, ModelEndOfStrokeStationary) {
+ PositionModeler modeler;
+ modeler.Reset({{4, -2}, {0, 0}, Time{0}}, PositionModelerParams());
+
+ std::vector<TipState> result;
+ modeler.ModelEndOfStroke({3, -1}, Duration(1. / 180), 20, .01,
+ std::back_inserter(result));
+ EXPECT_THAT(
+ result,
+ ElementsAre(
+ TipStateNear({{3.9091, -1.9091}, {-16.3636, 16.3636}, Time{0.0056}},
+ kTol),
+ TipStateNear({{3.7719, -1.7719}, {-24.6942, 24.6942}, Time{0.0111}},
+ kTol),
+ TipStateNear({{3.6194, -1.6194}, {-27.4476, 27.4476}, Time{0.0167}},
+ kTol),
+ TipStateNear({{3.4716, -1.4716}, {-26.6045, 26.6044}, Time{0.0222}},
+ kTol),
+ TipStateNear({{3.3401, -1.3401}, {-23.6799, 23.6799}, Time{0.0278}},
+ kTol),
+ TipStateNear({{3.2302, -1.2302}, {-19.7725, 19.7725}, Time{0.0333}},
+ kTol),
+ TipStateNear({{3.1434, -1.1434}, {-15.6306, 15.6306}, Time{0.0389}},
+ kTol),
+ TipStateNear({{3.0782, -1.0782}, {-11.7244, 11.7244}, Time{0.0444}},
+ kTol),
+ TipStateNear({{3.0320, -1.0320}, {-8.3149, 8.3149}, Time{0.0500}},
+ kTol),
+ TipStateNear({{3.0014, -1.0014}, {-5.5133, 5.5133}, Time{0.0556}},
+ kTol)));
+}
+
+TEST(PositionModelerTest, ModelEndOfStrokeInMotion) {
+ PositionModeler modeler;
+ modeler.Reset({{-1, 2}, {40, 10}, Time{1}}, PositionModelerParams());
+
+ std::vector<TipState> result;
+ modeler.ModelEndOfStroke({7, 2}, Duration(1. / 120), 20, .01,
+ std::back_inserter(result));
+ EXPECT_THAT(
+ result,
+ ElementsAre(
+ TipStateNear({{0.7697, 2.0333}, {212.3636, 4.0000}, Time{1.0083}},
+ kTol),
+ TipStateNear({{2.7520, 2.0398}, {237.8711, 0.7818}, Time{1.0167}},
+ kTol),
+ TipStateNear({{4.4138, 2.0343}, {199.4186, -0.6654}, Time{1.0250}},
+ kTol),
+ TipStateNear({{5.6075, 2.0251}, {143.2474, -1.1081}, Time{1.0333}},
+ kTol),
+ TipStateNear({{6.3698, 2.0162}, {91.4784, -1.0586}, Time{1.0417}},
+ kTol),
+ TipStateNear({{6.8037, 2.0094}, {52.0592, -0.8222}, Time{1.0500}},
+ kTol),
+ TipStateNear({{6.9655, 2.0065}, {38.8512, -0.6909}, Time{1.0542}},
+ kTol),
+ TipStateNear({{6.9850, 2.0062}, {37.4471, -0.6750}, Time{1.0547}},
+ kTol)));
+}
+
+TEST(PositionModelerTest, ModelEndOfStrokeMaxIterationsReached) {
+ PositionModeler modeler;
+ modeler.Reset({{8, -3}, {-100, -150}, Time{1}}, PositionModelerParams());
+
+ std::vector<TipState> result;
+ modeler.ModelEndOfStroke({-9, -10}, Duration(.0001), 10, .001,
+ std::back_inserter(result));
+ EXPECT_THAT(
+ result,
+ ElementsAre(
+ TipStateNear(
+ {{7.9896, -3.0151}, {-104.2873, -150.9818}, Time{1.0001}}, kTol),
+ TipStateNear(
+ {{7.9787, -3.0303}, {-108.5406, -151.9521}, Time{1.0002}}, kTol),
+ TipStateNear(
+ {{7.9674, -3.0456}, {-112.7601, -152.9110}, Time{1.0003}}, kTol),
+ TipStateNear(
+ {{7.9557, -3.0610}, {-116.9459, -153.8584}, Time{1.0004}}, kTol),
+ TipStateNear(
+ {{7.9436, -3.0764}, {-121.0982, -154.7945}, Time{1.0005}}, kTol),
+ TipStateNear(
+ {{7.9311, -3.0920}, {-125.2169, -155.7193}, Time{1.0006}}, kTol),
+ TipStateNear(
+ {{7.9182, -3.1077}, {-129.3023, -156.6328}, Time{1.0007}}, kTol),
+ TipStateNear(
+ {{7.9048, -3.1234}, {-133.3545, -157.5351}, Time{1.0008}}, kTol),
+ TipStateNear(
+ {{7.8911, -3.1393}, {-137.3736, -158.4263}, Time{1.0009}}, kTol),
+ TipStateNear(
+ {{7.8770, -3.1552}, {-141.3597, -159.3065}, Time{1.0010}},
+ kTol)));
+}
+
+} // namespace
+} // namespace stroke_model
+} // namespace ink