| 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
5 #include "flutter/impeller/geometry/geometry_asserts.h"
6 #include "gtest/gtest.h"
7
8 #include "flutter/impeller/geometry/arc.h"
9 #include "flutter/impeller/tessellator/tessellator.h"
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) {
30 EXPECT_POINT_NEAR(arc_iteration.start, impeller::Matrix::CosSin(start))
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
arc.h
impeller::Tessellator::Trigs
Definition: tessellator.h:48
impeller::Tessellator::Trigs::size
size_t size() const
Definition: tessellator.h:53
impeller::Tessellator::Trigs::GetSteps
size_t GetSteps() const
Definition: tessellator.h:58
impeller::Tessellator
A utility that generates triangles of the specified fill type given a polyline. This happens on the C...
Definition: tessellator.h:37
impeller::Tessellator::GetTrigsForDeviceRadius
Trigs GetTrigsForDeviceRadius(Scalar pixel_radius)
Definition: tessellator.cc:310
geometry_asserts.h
EXPECT_POINT_NEAR
#define EXPECT_POINT_NEAR(a, b)
Definition: geometry_asserts.h:205
impeller::testing::TEST
TEST(AllocationSizeTest, CanCreateTypedAllocations)
Definition: allocation_size_unittests.cc:10
impeller
Definition: allocation.cc:12
impeller::Vector2
Point Vector2
Definition: point.h:331
impeller::Scalar
float Scalar
Definition: scalar.h:19
impeller::kPiOver2
constexpr float kPiOver2
Definition: constants.h:32
impeller::Arc::Iteration::Quadrant::axis
impeller::Vector2 axis
Definition: arc.h:46
impeller::Arc::Iteration::Quadrant::start_index
size_t start_index
Definition: arc.h:47
impeller::Arc::Iteration::Quadrant::end_index
size_t end_index
Definition: arc.h:48
impeller::Arc::Iteration
Definition: arc.h:42
impeller::Arc::Iteration::end
impeller::Vector2 end
Definition: arc.h:59
impeller::Arc::Iteration::start
impeller::Vector2 start
Definition: arc.h:58
impeller::Arc::Iteration::quadrants
Quadrant quadrants[9]
Definition: arc.h:86
impeller::Arc::Iteration::quadrant_count
size_t quadrant_count
Definition: arc.h:63
impeller::Arc
Definition: arc.h:13
impeller::Arc::ComputeIterations
Iteration ComputeIterations(size_t step_count, bool simplify_360=true) const
Definition: arc.cc:102
impeller::Degrees
Definition: scalar.h:76
impeller::Degrees::degrees
Scalar degrees
Definition: scalar.h:77
impeller::Matrix::CosSin
static Vector2 CosSin(Radians radians)
Definition: matrix.h:618
impeller::TPoint::Cross
constexpr Type Cross(const TPoint &p) const
Definition: point.h:218
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
tessellator.h
start
const size_t start
Definition: tessellator_libtess.cc:62