77#include " impeller/core/buffer_view.h"
88#include " impeller/core/formats.h"
99#include " impeller/entity/geometry/geometry.h"
10+ #include " impeller/geometry/constants.h"
1011#include " impeller/geometry/path_builder.h"
1112#include " impeller/geometry/path_component.h"
1213
@@ -90,7 +91,7 @@ class StrokeGenerator {
9091 previous_offset = offset;
9192 offset = ComputeOffset (contour_start_point_i, contour_start_point_i,
9293 contour_end_point_i, contour);
93- const Point contour_first_offset = offset;
94+ const Point contour_first_offset = offset. GetVector () ;
9495
9596 if (contour_i > 0 ) {
9697 // This branch only executes when we've just finished drawing a contour
@@ -155,19 +156,52 @@ class StrokeGenerator {
155156 cap_proc (vtx_builder, polyline.GetPoint (contour_end_point_i - 1 ),
156157 cap_offset, scale, /* reverse=*/ false );
157158 } else {
158- join_proc (vtx_builder, polyline.GetPoint (contour_start_point_i), offset,
159- contour_first_offset, scaled_miter_limit, scale);
159+ join_proc (vtx_builder, polyline.GetPoint (contour_start_point_i),
160+ offset.GetVector (), contour_first_offset, scaled_miter_limit,
161+ scale);
160162 }
161163 }
162164 }
163165
166+ // / @brief Represents a ray in 2D space.
167+ // /
168+ // / This is a simple convenience struct for handling polyline offset
169+ // / values when generating stroke geometry. For performance reasons,
170+ // / it's sometimes adventageous to track the direction and magnitude
171+ // / for offsets separately.
172+ struct Ray {
173+ // / The normalized direction of the ray.
174+ Vector2 direction;
175+
176+ // / The magnitude of the ray.
177+ Scalar magnitude = 0.0 ;
178+
179+ // / Returns the vector representation of the ray.
180+ Vector2 GetVector () const { return direction * magnitude; }
181+
182+ // / Returns the scalar alignment of the two rays.
183+ // /
184+ // / Domain: [-1, 1]
185+ // / A value of 1 indicates the rays are parallel and pointing in the same
186+ // / direction. A value of -1 indicates the rays are parallel and pointing in
187+ // / opposite directions. A value of 0 indicates the rays are perpendicular.
188+ Scalar GetAlignment (const Ray& other) const {
189+ return direction.Dot (other.direction );
190+ }
191+
192+ // / Returns the scalar angle between the two rays.
193+ Radians AngleTo (const Ray& other) const {
194+ return direction.AngleTo (other.direction );
195+ }
196+ };
197+
164198 // / Computes offset by calculating the direction from point_i - 1 to point_i
165199 // / if point_i is within `contour_start_point_i` and `contour_end_point_i`;
166200 // / Otherwise, it uses direction from contour.
167- Point ComputeOffset (const size_t point_i,
168- const size_t contour_start_point_i,
169- const size_t contour_end_point_i,
170- const Path::PolylineContour& contour) const {
201+ Ray ComputeOffset (const size_t point_i,
202+ const size_t contour_start_point_i,
203+ const size_t contour_end_point_i,
204+ const Path::PolylineContour& contour) const {
171205 Point direction;
172206 if (point_i >= contour_end_point_i) {
173207 direction = contour.end_direction ;
@@ -177,7 +211,8 @@ class StrokeGenerator {
177211 direction = (polyline.GetPoint (point_i) - polyline.GetPoint (point_i - 1 ))
178212 .Normalize ();
179213 }
180- return Vector2{-direction.y , direction.x } * stroke_width * 0 .5f ;
214+ return {.direction = Vector2{-direction.y , direction.x },
215+ .magnitude = stroke_width * 0 .5f };
181216 }
182217
183218 void AddVerticesForLinearComponent (VertexWriter& vtx_builder,
@@ -192,25 +227,29 @@ class StrokeGenerator {
192227 for (size_t point_i = component_start_index; point_i < component_end_index;
193228 point_i++) {
194229 bool is_end_of_component = point_i == component_end_index - 1 ;
195- vtx.position = polyline.GetPoint (point_i) + offset;
230+
231+ Point offset_vector = offset.GetVector ();
232+
233+ vtx.position = polyline.GetPoint (point_i) + offset_vector;
196234 vtx_builder.AppendVertex (vtx.position );
197- vtx.position = polyline.GetPoint (point_i) - offset ;
235+ vtx.position = polyline.GetPoint (point_i) - offset_vector ;
198236 vtx_builder.AppendVertex (vtx.position );
199237
200238 // For line components, two additional points need to be appended
201239 // prior to appending a join connecting the next component.
202- vtx.position = polyline.GetPoint (point_i + 1 ) + offset ;
240+ vtx.position = polyline.GetPoint (point_i + 1 ) + offset_vector ;
203241 vtx_builder.AppendVertex (vtx.position );
204- vtx.position = polyline.GetPoint (point_i + 1 ) - offset ;
242+ vtx.position = polyline.GetPoint (point_i + 1 ) - offset_vector ;
205243 vtx_builder.AppendVertex (vtx.position );
206244
207245 previous_offset = offset;
208246 offset = ComputeOffset (point_i + 2 , contour_start_point_i,
209247 contour_end_point_i, contour);
210248 if (!is_last_component && is_end_of_component) {
211249 // Generate join from the current line to the next line.
212- join_proc (vtx_builder, polyline.GetPoint (point_i + 1 ), previous_offset,
213- offset, scaled_miter_limit, scale);
250+ join_proc (vtx_builder, polyline.GetPoint (point_i + 1 ),
251+ previous_offset.GetVector (), offset.GetVector (),
252+ scaled_miter_limit, scale);
214253 }
215254 }
216255 }
@@ -228,14 +267,44 @@ class StrokeGenerator {
228267 point_i++) {
229268 bool is_end_of_component = point_i == component_end_index - 1 ;
230269
231- vtx.position = polyline.GetPoint (point_i) + offset;
270+ vtx.position = polyline.GetPoint (point_i) + offset. GetVector () ;
232271 vtx_builder.AppendVertex (vtx.position );
233- vtx.position = polyline.GetPoint (point_i) - offset;
272+ vtx.position = polyline.GetPoint (point_i) - offset. GetVector () ;
234273 vtx_builder.AppendVertex (vtx.position );
235274
236275 previous_offset = offset;
237276 offset = ComputeOffset (point_i + 2 , contour_start_point_i,
238277 contour_end_point_i, contour);
278+
279+ // If the angle to the next segment is too sharp, round out the join.
280+ if (!is_end_of_component) {
281+ constexpr Scalar kAngleThreshold = 10 * kPi / 180 ;
282+ // `std::cosf` is not constexpr-able, unfortunately, so we have to bake
283+ // the alignment constant.
284+ constexpr Scalar kAlignmentThreshold =
285+ 0.984807753012208 ; // std::cosf(kThresholdAngle) -- 10 degrees
286+
287+ // Use a cheap dot product to determine whether the angle is too sharp.
288+ if (previous_offset.GetAlignment (offset) < kAlignmentThreshold ) {
289+ Scalar angle_total = previous_offset.AngleTo (offset).radians ;
290+ Scalar angle = kAngleThreshold ;
291+
292+ // Bridge the large angle with additional geometry at
293+ // `kAngleThreshold` interval.
294+ while (angle < std::abs (angle_total)) {
295+ Scalar signed_angle = angle_total < 0 ? -angle : angle;
296+ Point offset =
297+ previous_offset.GetVector ().Rotate (Radians (signed_angle));
298+ vtx.position = polyline.GetPoint (point_i) + offset;
299+ vtx_builder.AppendVertex (vtx.position );
300+ vtx.position = polyline.GetPoint (point_i) - offset;
301+ vtx_builder.AppendVertex (vtx.position );
302+
303+ angle += kAngleThreshold ;
304+ }
305+ }
306+ }
307+
239308 // For curve components, the polyline is detailed enough such that
240309 // it can avoid worrying about joins altogether.
241310 if (is_end_of_component) {
@@ -245,16 +314,18 @@ class StrokeGenerator {
245314 // `ComputeOffset` returns the contour's end direction when attempting
246315 // to grab offsets past `contour_end_point_i`, so just use `offset` when
247316 // we're on the last component.
248- Point last_component_offset =
249- is_last_component ? offset : previous_offset;
317+ Point last_component_offset = is_last_component
318+ ? offset.GetVector ()
319+ : previous_offset.GetVector ();
250320 vtx.position = polyline.GetPoint (point_i + 1 ) + last_component_offset;
251321 vtx_builder.AppendVertex (vtx.position );
252322 vtx.position = polyline.GetPoint (point_i + 1 ) - last_component_offset;
253323 vtx_builder.AppendVertex (vtx.position );
254324 // Generate join from the current line to the next line.
255325 if (!is_last_component) {
256326 join_proc (vtx_builder, polyline.GetPoint (point_i + 1 ),
257- previous_offset, offset, scaled_miter_limit, scale);
327+ previous_offset.GetVector (), offset.GetVector (),
328+ scaled_miter_limit, scale);
258329 }
259330 }
260331 }
@@ -267,8 +338,8 @@ class StrokeGenerator {
267338 const CapProc<VertexWriter>& cap_proc;
268339 const Scalar scale;
269340
270- Point previous_offset;
271- Point offset;
341+ Ray previous_offset;
342+ Ray offset;
272343 SolidFillVertexShader::PerVertexData vtx;
273344};
274345
0 commit comments