1212#include " impeller/entity/contents/solid_stroke_contents.h"
1313#include " impeller/entity/entity.h"
1414#include " impeller/entity/entity_playground.h"
15+ #include " impeller/geometry/geometry_unittests.h"
1516#include " impeller/geometry/path_builder.h"
1617#include " impeller/playground/playground.h"
1718#include " impeller/playground/widgets.h"
@@ -100,76 +101,89 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) {
100101 ImGui::SetNextWindowPos (
101102 {0 * padding.x + margin.x , 1 .7f * padding.y + margin.y });
102103 }
103- ImGui::Begin ( " Controls " );
104+
104105 // Slightly above sqrt(2) by default, so that right angles are just below
105106 // the limit and acute angles are over the limit (causing them to get
106107 // beveled).
107108 static Scalar miter_limit = 1.41421357 ;
108109 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+ }
114119 }
115120 ImGui::End ();
116121
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) {
119125 auto contents = std::make_unique<SolidStrokeContents>();
120126 contents->SetColor (Color::Red ().Premultiply ());
121127 contents->SetStrokeSize (width);
122128 contents->SetStrokeCap (cap);
123129 contents->SetStrokeJoin (join);
124130 contents->SetStrokeMiter (miter_limit);
125- return contents;
126- };
127131
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+ };
129149
130150 const Point a_def (0 , 0 ), b_def (0 , 100 ), c_def (150 , 0 ), d_def (150 , -100 ),
131151 e_def (75 , 75 );
132152 const Scalar r = 30 ;
133153 // Cap::kButt demo.
134154 {
135155 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 );
145163 }
146164
147165 // Cap::kSquare demo.
148166 {
149167 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 );
159175 }
160176
161177 // Cap::kRound demo.
162178 {
163179 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 );
173187 }
174188
175189 // Join::kBevel demo.
@@ -178,11 +192,9 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) {
178192 Point a = IMPELLER_PLAYGROUND_POINT (off + a_def, r, Color::White ());
179193 Point b = IMPELLER_PLAYGROUND_POINT (off + e_def, r, Color::White ());
180194 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 );
186198 }
187199
188200 // Join::kMiter demo.
@@ -191,11 +203,9 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) {
191203 Point a = IMPELLER_PLAYGROUND_POINT (off + a_def, r, Color::White ());
192204 Point b = IMPELLER_PLAYGROUND_POINT (off + e_def, r, Color::White ());
193205 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 );
199209 }
200210
201211 // Join::kRound demo.
@@ -204,11 +214,9 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) {
204214 Point a = IMPELLER_PLAYGROUND_POINT (off + a_def, r, Color::White ());
205215 Point b = IMPELLER_PLAYGROUND_POINT (off + e_def, r, Color::White ());
206216 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 );
212220 }
213221
214222 return true ;
@@ -789,5 +797,53 @@ TEST_F(EntityTest, ContentsGetBoundsForEmptyPathReturnsNullopt) {
789797 ASSERT_FALSE (entity.GetPathCoverage ().has_value ());
790798}
791799
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+
792848} // namespace testing
793849} // namespace impeller
0 commit comments