Flutter Impeller
arc_unittests.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
6 #include "gtest/gtest.h"
7 
10 
11 namespace impeller {
12 namespace testing {
13 
14 namespace {
15 
16 // Tests the basic relationships of the angles iterated according to the
17 // rules of the ArcIteration struct. Each step iterated should be just
18 // about the same angular distance from the previous step, computed using
19 // the Cross product of the adjacent vectors, which should be the same as
20 // the sine of the angle between them when using unit vectors.
21 //
22 // Special support for shorter starting and ending steps to bridge the gap
23 // from the true arc start and the true arc end and the first and last
24 // angles iterated from the trigs.
25 void TestArcIterator(const impeller::Arc::Iteration arc_iteration,
26  const impeller::Tessellator::Trigs& trigs,
27  Degrees start,
28  Degrees sweep,
29  const std::string& label) {
31  << label;
32  EXPECT_POINT_NEAR(arc_iteration.end, impeller::Matrix::CosSin(start + sweep))
33  << label;
34  if (arc_iteration.quadrant_count == 0u) {
35  // There is just the begin and end angle and there are no constraints
36  // on how far apart they should be, but the end vector should be
37  // non-counterclockwise from the start vector.
38  EXPECT_GE(arc_iteration.start.Cross(arc_iteration.end), 0.0f);
39  return;
40  }
41 
42  const size_t steps = trigs.size() - 1;
43  const Scalar step_angle = kPiOver2 / steps;
44 
45  // The first and last steps are allowed to be from 0.1 to 1.1 in size
46  // as we don't want to iterate an extra step that is less than 0.1 steps
47  // from the begin/end angles. We use min/max values that are ever so
48  // slightly larger than that to avoid round-off errors.
49  const Scalar edge_min_cross = std::sin(step_angle * 0.099f);
50  const Scalar edge_max_cross = std::sin(step_angle * 1.101f);
51  const Scalar typical_min_cross = std::sin(step_angle * 0.999f);
52  const Scalar typical_max_cross = std::sin(step_angle * 1.001f);
53 
54  Vector2 cur_vector;
55  auto trace = [&cur_vector](Vector2 vector, Scalar min_cross, Scalar max_cross,
56  const std::string& label) -> void {
57  EXPECT_GT(cur_vector.Cross(vector), min_cross) << label;
58  EXPECT_LT(cur_vector.Cross(vector), max_cross) << label;
59  cur_vector = vector;
60  };
61 
62  // The first edge encountered in the loop should be judged by the edge
63  // conditions. After that the steps derived from the Trigs should be
64  // judged by the typical min/max values.
65  Scalar min_cross = edge_min_cross;
66  Scalar max_cross = edge_max_cross;
67 
68  cur_vector = arc_iteration.start;
69  for (size_t i = 0; i < arc_iteration.quadrant_count; i++) {
70  auto& quadrant = arc_iteration.quadrants[i];
71  EXPECT_LT(quadrant.start_index, quadrant.end_index)
72  << label << ", quadrant: " << i;
73  for (size_t j = quadrant.start_index; j < quadrant.end_index; j++) {
74  trace(trigs[j] * quadrant.axis, min_cross, max_cross,
75  label + ", quadrant: " + std::to_string(i) +
76  ", step: " + std::to_string(j));
77  // At this point we can guarantee that we've already used the initial
78  // min/max values, now replace them with the typical values.
79  min_cross = typical_min_cross;
80  max_cross = typical_max_cross;
81  }
82  }
83 
84  // The jump to the end angle should be judged by the edge conditions.
85  trace(arc_iteration.end, edge_min_cross, edge_max_cross,
86  label + " step to end");
87 }
88 
89 void TestFullCircleArc(Degrees start, Degrees sweep) {
90  auto label =
91  std::to_string(start.degrees) + " += " + std::to_string(sweep.degrees);
92 
93  Arc arc(Rect::MakeLTRB(10, 10, 20, 20), Degrees(start), Degrees(sweep),
94  false);
95 
96  Tessellator tessellator;
97  const auto trigs = tessellator.GetTrigsForDeviceRadius(100);
98  size_t steps = trigs.GetSteps();
99  const auto& arc_iteration = arc.ComputeIterations(steps);
100 
101  EXPECT_EQ(arc_iteration.start, Vector2(1.0f, 0.0f)) << label;
102  EXPECT_EQ(arc_iteration.quadrant_count, 4u) << label;
103  EXPECT_EQ(arc_iteration.quadrants[0].axis, Vector2(1.0f, 0.0f)) << label;
104  EXPECT_EQ(arc_iteration.quadrants[0].start_index, 1u) << label;
105  EXPECT_EQ(arc_iteration.quadrants[0].end_index, steps) << label;
106  EXPECT_EQ(arc_iteration.quadrants[1].axis, Vector2(0.0f, 1.0f)) << label;
107  EXPECT_EQ(arc_iteration.quadrants[1].start_index, 0u) << label;
108  EXPECT_EQ(arc_iteration.quadrants[1].end_index, steps) << label;
109  EXPECT_EQ(arc_iteration.quadrants[2].axis, Vector2(-1.0f, 0.0f)) << label;
110  EXPECT_EQ(arc_iteration.quadrants[2].start_index, 0u) << label;
111  EXPECT_EQ(arc_iteration.quadrants[2].end_index, steps) << label;
112  EXPECT_EQ(arc_iteration.quadrants[3].axis, Vector2(0.0f, -1.0f)) << label;
113  EXPECT_EQ(arc_iteration.quadrants[3].start_index, 0u) << label;
114  EXPECT_EQ(arc_iteration.quadrants[3].end_index, steps) << label;
115  EXPECT_EQ(arc_iteration.end, Vector2(1.0f, 0.0f)) << label;
116 
117  // For full circle arcs the original start and sweep are ignored and it
118  // returns an iterator that always goes from 0->360.
119  TestArcIterator(arc_iteration, trigs, Degrees(0), Degrees(360),
120  "Full Circle(" + label + ")");
121 }
122 
123 } // namespace
124 
125 TEST(ArcTest, ArcIterationsFullCircle) {
126  // Anything with a sweep <=-360 or >=360 is a full circle regardless of
127  // starting angle
128  for (int start = -720; start < 720; start += 30) {
129  for (int sweep = 360; sweep < 1080; sweep += 45) {
130  TestFullCircleArc(Degrees(start), Degrees(sweep));
131  TestFullCircleArc(Degrees(start), Degrees(-sweep));
132  }
133  }
134 }
135 
136 namespace {
137 static void CheckOneQuadrant(Degrees start, Degrees sweep) {
138  Arc arc(Rect::MakeLTRB(10, 10, 20, 20), start, sweep, false);
139  Tessellator tessellator;
140  const auto trigs = tessellator.GetTrigsForDeviceRadius(100);
141  const auto& arc_iteration = arc.ComputeIterations(trigs.GetSteps());
142 
143  EXPECT_POINT_NEAR(arc_iteration.start, Matrix::CosSin(start));
144  EXPECT_EQ(arc_iteration.quadrant_count, 1u);
145  EXPECT_POINT_NEAR(arc_iteration.end, Matrix::CosSin(start + sweep));
146 
147  std::string label = "Quadrant(" + std::to_string(start.degrees) +
148  " += " + std::to_string(sweep.degrees) + ")";
149  TestArcIterator(arc_iteration, trigs, start, sweep, label);
150 }
151 } // namespace
152 
153 TEST(ArcTest, ArcIterationsVariousStartAnglesNearQuadrantAxis) {
154  Tessellator tessellator;
155  const auto trigs = tessellator.GetTrigsForDeviceRadius(100);
156  const Degrees sweep(45);
157 
158  for (int start_i = -1000; start_i < 1000; start_i += 5) {
159  Scalar start_degrees = start_i * 0.01f;
160  for (int quadrant = -360; quadrant <= 360; quadrant += 90) {
161  const Degrees start(quadrant + start_degrees);
162  Arc arc(Rect::MakeLTRB(10, 10, 20, 20), start, sweep, false);
163  const auto& arc_iteration = arc.ComputeIterations(trigs.GetSteps());
164 
165  TestArcIterator(arc_iteration, trigs, start, sweep,
166  "Various angles(" + std::to_string(start.degrees) +
167  " += " + std::to_string(sweep.degrees));
168  }
169  }
170 }
171 
172 TEST(ArcTest, ArcIterationsVariousEndAnglesNearQuadrantAxis) {
173  Tessellator tessellator;
174  const auto trigs = tessellator.GetTrigsForDeviceRadius(100);
175 
176  for (int sweep_i = 5; sweep_i < 20000; sweep_i += 5) {
177  const Degrees sweep(sweep_i * 0.01f);
178  for (int quadrant = -360; quadrant <= 360; quadrant += 90) {
179  const Degrees start(quadrant + 80);
180  Arc arc(Rect::MakeLTRB(10, 10, 20, 20), start, sweep, false);
181  const auto& arc_iteration = arc.ComputeIterations(trigs.GetSteps());
182 
183  TestArcIterator(arc_iteration, trigs, start, sweep,
184  "Various angles(" + std::to_string(start.degrees) +
185  " += " + std::to_string(sweep.degrees));
186  }
187  }
188 }
189 
190 TEST(ArcTest, ArcIterationsVariousTinyArcsNearQuadrantAxis) {
191  Tessellator tessellator;
192  const auto trigs = tessellator.GetTrigsForDeviceRadius(100);
193  const Degrees sweep(0.1f);
194 
195  for (int start_i = -1000; start_i < 1000; start_i += 5) {
196  Scalar start_degrees = start_i * 0.01f;
197  for (int quadrant = -360; quadrant <= 360; quadrant += 90) {
198  const Degrees start(quadrant + start_degrees);
199  Arc arc(Rect::MakeLTRB(10, 10, 20, 20), start, sweep, false);
200  const auto& arc_iteration = arc.ComputeIterations(trigs.GetSteps());
201  ASSERT_EQ(arc_iteration.quadrant_count, 0u);
202 
203  TestArcIterator(arc_iteration, trigs, start, sweep,
204  "Various angles(" + std::to_string(start.degrees) +
205  " += " + std::to_string(sweep.degrees));
206  }
207  }
208 }
209 
210 TEST(ArcTest, ArcIterationsOnlyFirstQuadrant) {
211  CheckOneQuadrant(Degrees(90 * 0 + 30), Degrees(30));
212 }
213 
214 TEST(ArcTest, ArcIterationsOnlySecondQuadrant) {
215  CheckOneQuadrant(Degrees(90 * 1 + 30), Degrees(30));
216 }
217 
218 TEST(ArcTest, ArcIterationsOnlyThirdQuadrant) {
219  CheckOneQuadrant(Degrees(90 * 2 + 30), Degrees(30));
220 }
221 
222 TEST(ArcTest, ArcIterationsOnlyFourthQuadrant) {
223  CheckOneQuadrant(Degrees(90 * 3 + 30), Degrees(30));
224 }
225 
226 namespace {
227 static void CheckFiveQuadrants(Degrees start, Degrees sweep) {
228  std::string label =
229  std::to_string(start.degrees) + " += " + std::to_string(sweep.degrees);
230 
231  Tessellator tessellator;
232  const auto trigs = tessellator.GetTrigsForDeviceRadius(100);
233  Arc arc(Rect::MakeLTRB(10, 10, 20, 20), start, sweep, false);
234  const auto& arc_iteration = arc.ComputeIterations(trigs.GetSteps());
235  size_t steps = trigs.size() - 1;
236 
237  EXPECT_POINT_NEAR(arc_iteration.start, Matrix::CosSin(start)) << label;
238  EXPECT_EQ(arc_iteration.quadrant_count, 5u) << label;
239 
240  // quadrant 0 start index depends on angle
241  EXPECT_EQ(arc_iteration.quadrants[0].end_index, steps) << label;
242 
243  EXPECT_EQ(arc_iteration.quadrants[1].start_index, 0u) << label;
244  EXPECT_EQ(arc_iteration.quadrants[1].end_index, steps) << label;
245 
246  EXPECT_EQ(arc_iteration.quadrants[2].start_index, 0u) << label;
247  EXPECT_EQ(arc_iteration.quadrants[2].end_index, steps) << label;
248 
249  EXPECT_EQ(arc_iteration.quadrants[3].start_index, 0u) << label;
250  EXPECT_EQ(arc_iteration.quadrants[3].end_index, steps) << label;
251 
252  EXPECT_EQ(arc_iteration.quadrants[4].start_index, 0u) << label;
253  // quadrant 4 end index depends on angle
254 
255  EXPECT_POINT_NEAR(arc_iteration.end, Matrix::CosSin(start + sweep)) << label;
256 
257  TestArcIterator(arc_iteration, trigs, start, sweep,
258  "Five quadrants(" + label + ")");
259 }
260 } // namespace
261 
262 TEST(ArcTest, ArcIterationsAllQuadrantsFromFirst) {
263  CheckFiveQuadrants(Degrees(90 * 0 + 60), Degrees(330));
264 }
265 
266 TEST(ArcTest, ArcIterationsAllQuadrantsFromSecond) {
267  CheckFiveQuadrants(Degrees(90 * 1 + 60), Degrees(330));
268 }
269 
270 TEST(ArcTest, ArcIterationsAllQuadrantsFromThird) {
271  CheckFiveQuadrants(Degrees(90 * 2 + 60), Degrees(330));
272 }
273 
274 TEST(ArcTest, ArcIterationsAllQuadrantsFromFourth) {
275  CheckFiveQuadrants(Degrees(90 * 3 + 60), Degrees(330));
276 }
277 
278 } // namespace testing
279 } // namespace impeller
A utility that generates triangles of the specified fill type given a polyline. This happens on the C...
Definition: tessellator.h:37
Trigs GetTrigsForDeviceRadius(Scalar pixel_radius)
Definition: tessellator.cc:310
#define EXPECT_POINT_NEAR(a, b)
TEST(AllocationSizeTest, CanCreateTypedAllocations)
Point Vector2
Definition: point.h:331
float Scalar
Definition: scalar.h:19
constexpr float kPiOver2
Definition: constants.h:32
impeller::Vector2 axis
Definition: arc.h:46
impeller::Vector2 end
Definition: arc.h:59
impeller::Vector2 start
Definition: arc.h:58
Quadrant quadrants[9]
Definition: arc.h:86
size_t quadrant_count
Definition: arc.h:63
Iteration ComputeIterations(size_t step_count, bool simplify_360=true) const
Definition: arc.cc:102
Scalar degrees
Definition: scalar.h:77
static Vector2 CosSin(Radians radians)
Definition: matrix.h:618
constexpr Type Cross(const TPoint &p) const
Definition: point.h:218
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
const size_t start