12
12
#include " impeller/entity/contents/solid_stroke_contents.h"
13
13
#include " impeller/entity/entity.h"
14
14
#include " impeller/entity/entity_playground.h"
15
+ #include " impeller/geometry/geometry_unittests.h"
15
16
#include " impeller/geometry/path_builder.h"
16
17
#include " impeller/playground/playground.h"
17
18
#include " impeller/playground/widgets.h"
@@ -100,76 +101,89 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) {
100
101
ImGui::SetNextWindowPos (
101
102
{0 * padding.x + margin.x , 1 .7f * padding.y + margin.y });
102
103
}
103
- ImGui::Begin ( " Controls " );
104
+
104
105
// Slightly above sqrt(2) by default, so that right angles are just below
105
106
// the limit and acute angles are over the limit (causing them to get
106
107
// beveled).
107
108
static Scalar miter_limit = 1.41421357 ;
108
109
static Scalar width = 30 ;
109
- ImGui::SliderFloat (" Miter limit" , &miter_limit, 0 , 30 );
110
- ImGui::SliderFloat (" Stroke width" , &width, 0 , 100 );
111
- if (ImGui::Button (" Reset" )) {
112
- miter_limit = 1.41421357 ;
113
- width = 30 ;
110
+
111
+ ImGui::Begin (" Controls" );
112
+ {
113
+ ImGui::SliderFloat (" Miter limit" , &miter_limit, 0 , 30 );
114
+ ImGui::SliderFloat (" Stroke width" , &width, 0 , 100 );
115
+ if (ImGui::Button (" Reset" )) {
116
+ miter_limit = 1.41421357 ;
117
+ width = 30 ;
118
+ }
114
119
}
115
120
ImGui::End ();
116
121
117
- auto create_contents = [width = width](SolidStrokeContents::Cap cap,
118
- SolidStrokeContents::Join join) {
122
+ auto render_path = [width = width, &context, &pass](
123
+ Path path, SolidStrokeContents::Cap cap,
124
+ SolidStrokeContents::Join join) {
119
125
auto contents = std::make_unique<SolidStrokeContents>();
120
126
contents->SetColor (Color::Red ().Premultiply ());
121
127
contents->SetStrokeSize (width);
122
128
contents->SetStrokeCap (cap);
123
129
contents->SetStrokeJoin (join);
124
130
contents->SetStrokeMiter (miter_limit);
125
- return contents;
126
- };
127
131
128
- Entity entity;
132
+ Entity entity;
133
+ entity.SetPath (path);
134
+ entity.SetContents (std::move (contents));
135
+
136
+ auto coverage = entity.GetCoverage ();
137
+ if (coverage.has_value ()) {
138
+ auto bounds_contents = std::make_unique<SolidColorContents>();
139
+ bounds_contents->SetColor (Color::Green ().WithAlpha (0.5 ));
140
+ Entity bounds_entity;
141
+ bounds_entity.SetPath (
142
+ PathBuilder{}.AddRect (entity.GetCoverage ().value ()).TakePath ());
143
+ bounds_entity.SetContents (std::move (bounds_contents));
144
+ bounds_entity.Render (context, pass);
145
+ }
146
+
147
+ entity.Render (context, pass);
148
+ };
129
149
130
150
const Point a_def (0 , 0 ), b_def (0 , 100 ), c_def (150 , 0 ), d_def (150 , -100 ),
131
151
e_def (75 , 75 );
132
152
const Scalar r = 30 ;
133
153
// Cap::kButt demo.
134
154
{
135
155
Point off = Point (0 , 0 ) * padding + margin;
136
- Point a, b, c, d;
137
- std::tie (a, b) = IMPELLER_PLAYGROUND_LINE (off + a_def, off + b_def, r,
138
- Color::Black (), Color::White ());
139
- std::tie (c, d) = IMPELLER_PLAYGROUND_LINE (off + c_def, off + d_def, r,
140
- Color::Black (), Color::White ());
141
- entity.SetPath (PathBuilder{}.AddCubicCurve (a, b, d, c).TakePath ());
142
- entity.SetContents (create_contents (SolidStrokeContents::Cap::kButt ,
143
- SolidStrokeContents::Join::kBevel ));
144
- entity.Render (context, pass);
156
+ auto [a, b] = IMPELLER_PLAYGROUND_LINE (off + a_def, off + b_def, r,
157
+ Color::Black (), Color::White ());
158
+ auto [c, d] = IMPELLER_PLAYGROUND_LINE (off + c_def, off + d_def, r,
159
+ Color::Black (), Color::White ());
160
+ render_path (PathBuilder{}.AddCubicCurve (a, b, d, c).TakePath (),
161
+ SolidStrokeContents::Cap::kButt ,
162
+ SolidStrokeContents::Join::kBevel );
145
163
}
146
164
147
165
// Cap::kSquare demo.
148
166
{
149
167
Point off = Point (1 , 0 ) * padding + margin;
150
- Point a, b, c, d;
151
- std::tie (a, b) = IMPELLER_PLAYGROUND_LINE (off + a_def, off + b_def, r,
152
- Color::Black (), Color::White ());
153
- std::tie (c, d) = IMPELLER_PLAYGROUND_LINE (off + c_def, off + d_def, r,
154
- Color::Black (), Color::White ());
155
- entity.SetPath (PathBuilder{}.AddCubicCurve (a, b, d, c).TakePath ());
156
- entity.SetContents (create_contents (SolidStrokeContents::Cap::kSquare ,
157
- SolidStrokeContents::Join::kBevel ));
158
- entity.Render (context, pass);
168
+ auto [a, b] = IMPELLER_PLAYGROUND_LINE (off + a_def, off + b_def, r,
169
+ Color::Black (), Color::White ());
170
+ auto [c, d] = IMPELLER_PLAYGROUND_LINE (off + c_def, off + d_def, r,
171
+ Color::Black (), Color::White ());
172
+ render_path (PathBuilder{}.AddCubicCurve (a, b, d, c).TakePath (),
173
+ SolidStrokeContents::Cap::kSquare ,
174
+ SolidStrokeContents::Join::kBevel );
159
175
}
160
176
161
177
// Cap::kRound demo.
162
178
{
163
179
Point off = Point (2 , 0 ) * padding + margin;
164
- Point a, b, c, d;
165
- std::tie (a, b) = IMPELLER_PLAYGROUND_LINE (off + a_def, off + b_def, r,
166
- Color::Black (), Color::White ());
167
- std::tie (c, d) = IMPELLER_PLAYGROUND_LINE (off + c_def, off + d_def, r,
168
- Color::Black (), Color::White ());
169
- entity.SetPath (PathBuilder{}.AddCubicCurve (a, b, d, c).TakePath ());
170
- entity.SetContents (create_contents (SolidStrokeContents::Cap::kRound ,
171
- SolidStrokeContents::Join::kBevel ));
172
- entity.Render (context, pass);
180
+ auto [a, b] = IMPELLER_PLAYGROUND_LINE (off + a_def, off + b_def, r,
181
+ Color::Black (), Color::White ());
182
+ auto [c, d] = IMPELLER_PLAYGROUND_LINE (off + c_def, off + d_def, r,
183
+ Color::Black (), Color::White ());
184
+ render_path (PathBuilder{}.AddCubicCurve (a, b, d, c).TakePath (),
185
+ SolidStrokeContents::Cap::kRound ,
186
+ SolidStrokeContents::Join::kBevel );
173
187
}
174
188
175
189
// Join::kBevel demo.
@@ -178,11 +192,9 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) {
178
192
Point a = IMPELLER_PLAYGROUND_POINT (off + a_def, r, Color::White ());
179
193
Point b = IMPELLER_PLAYGROUND_POINT (off + e_def, r, Color::White ());
180
194
Point c = IMPELLER_PLAYGROUND_POINT (off + c_def, r, Color::White ());
181
- entity.SetPath (
182
- PathBuilder{}.MoveTo (a).LineTo (b).LineTo (c).Close ().TakePath ());
183
- entity.SetContents (create_contents (SolidStrokeContents::Cap::kButt ,
184
- SolidStrokeContents::Join::kBevel ));
185
- entity.Render (context, pass);
195
+ render_path (
196
+ PathBuilder{}.MoveTo (a).LineTo (b).LineTo (c).Close ().TakePath (),
197
+ SolidStrokeContents::Cap::kButt , SolidStrokeContents::Join::kBevel );
186
198
}
187
199
188
200
// Join::kMiter demo.
@@ -191,11 +203,9 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) {
191
203
Point a = IMPELLER_PLAYGROUND_POINT (off + a_def, r, Color::White ());
192
204
Point b = IMPELLER_PLAYGROUND_POINT (off + e_def, r, Color::White ());
193
205
Point c = IMPELLER_PLAYGROUND_POINT (off + c_def, r, Color::White ());
194
- entity.SetPath (
195
- PathBuilder{}.MoveTo (a).LineTo (b).LineTo (c).Close ().TakePath ());
196
- entity.SetContents (create_contents (SolidStrokeContents::Cap::kButt ,
197
- SolidStrokeContents::Join::kMiter ));
198
- entity.Render (context, pass);
206
+ render_path (
207
+ PathBuilder{}.MoveTo (a).LineTo (b).LineTo (c).Close ().TakePath (),
208
+ SolidStrokeContents::Cap::kButt , SolidStrokeContents::Join::kMiter );
199
209
}
200
210
201
211
// Join::kRound demo.
@@ -204,11 +214,9 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) {
204
214
Point a = IMPELLER_PLAYGROUND_POINT (off + a_def, r, Color::White ());
205
215
Point b = IMPELLER_PLAYGROUND_POINT (off + e_def, r, Color::White ());
206
216
Point c = IMPELLER_PLAYGROUND_POINT (off + c_def, r, Color::White ());
207
- entity.SetPath (
208
- PathBuilder{}.MoveTo (a).LineTo (b).LineTo (c).Close ().TakePath ());
209
- entity.SetContents (create_contents (SolidStrokeContents::Cap::kButt ,
210
- SolidStrokeContents::Join::kRound ));
211
- entity.Render (context, pass);
217
+ render_path (
218
+ PathBuilder{}.MoveTo (a).LineTo (b).LineTo (c).Close ().TakePath (),
219
+ SolidStrokeContents::Cap::kButt , SolidStrokeContents::Join::kRound );
212
220
}
213
221
214
222
return true ;
@@ -789,5 +797,53 @@ TEST_F(EntityTest, ContentsGetBoundsForEmptyPathReturnsNullopt) {
789
797
ASSERT_FALSE (entity.GetPathCoverage ().has_value ());
790
798
}
791
799
800
+ TEST_F (EntityTest, SolidStrokeCoverageIsCorrect) {
801
+ {
802
+ Entity entity;
803
+ auto contents = std::make_unique<SolidStrokeContents>();
804
+ contents->SetStrokeCap (SolidStrokeContents::Cap::kButt );
805
+ contents->SetStrokeJoin (SolidStrokeContents::Join::kBevel );
806
+ contents->SetStrokeSize (4 );
807
+ entity.SetPath (PathBuilder{}.AddLine ({0 , 0 }, {10 , 10 }).TakePath ());
808
+ entity.SetContents (std::move (contents));
809
+ auto actual = entity.GetCoverage ();
810
+ auto expected = Rect::MakeLTRB (-2 , -2 , 12 , 12 );
811
+ ASSERT_TRUE (actual.has_value ());
812
+ ASSERT_RECT_NEAR (actual.value (), expected);
813
+ }
814
+
815
+ // Cover the Cap::kSquare case.
816
+ {
817
+ Entity entity;
818
+ auto contents = std::make_unique<SolidStrokeContents>();
819
+ contents->SetStrokeCap (SolidStrokeContents::Cap::kSquare );
820
+ contents->SetStrokeJoin (SolidStrokeContents::Join::kBevel );
821
+ contents->SetStrokeSize (4 );
822
+ entity.SetPath (PathBuilder{}.AddLine ({0 , 0 }, {10 , 10 }).TakePath ());
823
+ entity.SetContents (std::move (contents));
824
+ auto actual = entity.GetCoverage ();
825
+ auto expected =
826
+ Rect::MakeLTRB (-sqrt (8 ), -sqrt (8 ), 10 + sqrt (8 ), 10 + sqrt (8 ));
827
+ ASSERT_TRUE (actual.has_value ());
828
+ ASSERT_RECT_NEAR (actual.value (), expected);
829
+ }
830
+
831
+ // Cover the Join::kMiter case.
832
+ {
833
+ Entity entity;
834
+ auto contents = std::make_unique<SolidStrokeContents>();
835
+ contents->SetStrokeCap (SolidStrokeContents::Cap::kSquare );
836
+ contents->SetStrokeJoin (SolidStrokeContents::Join::kMiter );
837
+ contents->SetStrokeSize (4 );
838
+ contents->SetStrokeMiter (2 );
839
+ entity.SetPath (PathBuilder{}.AddLine ({0 , 0 }, {10 , 10 }).TakePath ());
840
+ entity.SetContents (std::move (contents));
841
+ auto actual = entity.GetCoverage ();
842
+ auto expected = Rect::MakeLTRB (-4 , -4 , 14 , 14 );
843
+ ASSERT_TRUE (actual.has_value ());
844
+ ASSERT_RECT_NEAR (actual.value (), expected);
845
+ }
846
+ }
847
+
792
848
} // namespace testing
793
849
} // namespace impeller
0 commit comments