Flutter Impeller
path_component.h
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 #ifndef FLUTTER_IMPELLER_GEOMETRY_PATH_COMPONENT_H_
6 #define FLUTTER_IMPELLER_GEOMETRY_PATH_COMPONENT_H_
7 
8 #include <array>
9 #include <functional>
10 #include <optional>
11 #include <type_traits>
12 #include <vector>
13 
16 
17 namespace impeller {
18 
19 /// @brief An interface for generating a multi contour polyline as a triangle
20 /// strip.
21 class VertexWriter {
22  public:
23  virtual void EndContour() = 0;
24 
25  virtual void Write(Point point) = 0;
26 };
27 
28 /// @brief A vertex writer that generates a triangle fan and requires primitive
29 /// restart.
30 class FanVertexWriter : public VertexWriter {
31  public:
32  explicit FanVertexWriter(Point* point_buffer, uint16_t* index_buffer);
33 
35 
36  size_t GetIndexCount() const;
37 
38  void EndContour() override;
39 
40  void Write(Point point) override;
41 
42  private:
43  size_t count_ = 0;
44  size_t index_count_ = 0;
45  Point* point_buffer_ = nullptr;
46  uint16_t* index_buffer_ = nullptr;
47 };
48 
49 /// @brief A vertex writer that generates a triangle strip and requires
50 /// primitive restart.
52  public:
53  explicit StripVertexWriter(Point* point_buffer, uint16_t* index_buffer);
54 
56 
57  size_t GetIndexCount() const;
58 
59  void EndContour() override;
60 
61  void Write(Point point) override;
62 
63  private:
64  size_t count_ = 0;
65  size_t index_count_ = 0;
66  size_t contour_start_ = 0;
67  Point* point_buffer_ = nullptr;
68  uint16_t* index_buffer_ = nullptr;
69 };
70 
71 /// @brief A vertex writer that generates a line strip topology.
73  public:
74  explicit LineStripVertexWriter(std::vector<Point>& points);
75 
77 
78  void EndContour() override;
79 
80  void Write(Point point) override;
81 
82  std::pair<size_t, size_t> GetVertexCount() const;
83 
84  const std::vector<Point>& GetOversizedBuffer() const;
85 
86  private:
87  size_t offset_ = 0u;
88  std::vector<Point>& points_;
89  std::vector<Point> overflow_;
90 };
91 
92 /// @brief A vertex writer that has no hardware requirements.
94  public:
95  explicit GLESVertexWriter(std::vector<Point>& points,
96  std::vector<uint16_t>& indices);
97 
98  ~GLESVertexWriter() = default;
99 
100  void EndContour() override;
101 
102  void Write(Point point) override;
103 
104  private:
105  bool previous_contour_odd_points_ = false;
106  size_t contour_start_ = 0u;
107  std::vector<Point>& points_;
108  std::vector<uint16_t>& indices_;
109 };
110 
114 
116 
117  LinearPathComponent(Point ap1, Point ap2) : p1(ap1), p2(ap2) {}
118 
119  Point Solve(Scalar time) const;
120 
121  void AppendPolylinePoints(std::vector<Point>& points) const;
122 
123  std::vector<Point> Extrema() const;
124 
125  bool operator==(const LinearPathComponent& other) const {
126  return p1 == other.p1 && p2 == other.p2;
127  }
128 
129  std::optional<Vector2> GetStartDirection() const;
130 
131  std::optional<Vector2> GetEndDirection() const;
132 };
133 
134 // A component that represets a Quadratic Bézier curve.
136  // Start point.
138  // Control point.
140  // End point.
142 
144 
146  : p1(ap1), cp(acp), p2(ap2) {}
147 
148  Point Solve(Scalar time) const;
149 
150  Point SolveDerivative(Scalar time) const;
151 
152  void AppendPolylinePoints(Scalar scale_factor,
153  std::vector<Point>& points) const;
154 
155  using PointProc = std::function<void(const Point& point)>;
156 
157  void ToLinearPathComponents(Scalar scale_factor, const PointProc& proc) const;
158 
159  void ToLinearPathComponents(Scalar scale, VertexWriter& writer) const;
160 
161  size_t CountLinearPathComponents(Scalar scale) const;
162 
163  std::vector<Point> Extrema() const;
164 
165  bool operator==(const QuadraticPathComponent& other) const {
166  return p1 == other.p1 && cp == other.cp && p2 == other.p2;
167  }
168 
169  std::optional<Vector2> GetStartDirection() const;
170 
171  std::optional<Vector2> GetEndDirection() const;
172 };
173 
174 // A component that represets a Conic section curve.
175 //
176 // A conic section is basically nearly a quadratic bezier curve, but it
177 // has an additional weight that is applied to the middle term (the control
178 // point term).
179 //
180 // Starting with the equation for a quadratic curve which is:
181 // (A) P1 * (1 - t) * (1 - t)
182 // + CP * 2 * t * (1 - t)
183 // + P2 * t * t
184 // One thing to note is that the quadratic coefficients always sum to 1:
185 // (B) (1-t)(1-t) + 2t(1-t) + tt
186 // == 1 - 2t + tt + 2t - 2tt + tt
187 // == 1
188 // which means that the resulting point is always a weighted average of
189 // the 3 points without having to "divide by the sum of the coefficients"
190 // that is normally done when computing weighted averages.
191 //
192 // The conic equation, though, would then be:
193 // (C) P1 * (1 - t) * (1 - t)
194 // + CP * 2 * t * (1 - t) * w
195 // + P2 * t * t
196 // That would be the final equation, but if we look at the coefficients:
197 // (D) (1-t)(1-t) + 2wt(1-t) + tt
198 // == 1 - 2t + tt + 2wt - 2wtt + tt
199 // == 1 + (2w - 2)t + (2 - 2w)tt
200 // These only sum to 1 if the weight (w) is 1. In order for this math to
201 // produce a point that is the weighted average of the 3 points, we have
202 // to compute both and divide the equation (C) by the equation (D).
203 //
204 // Note that there are important potential optimizations we could apply.
205 // If w is 0,
206 // then this equation devolves into a straight line from P1 to P2.
207 // Note that the "progress" from P1 to P2, as a function of t, is
208 // quadratic if you compute it as the indicated numerator and denominator,
209 // but the actual points generated are all on the line from P1 to P2
210 // If w is (sqrt(2) / 2),
211 // then this math is exactly an elliptical section, provided the 3 points
212 // P1, CP, P2 form a right angle, and a circular section if they are also
213 // of equal length (|P1,CP| == |CP,P2|)
214 // If w is 1,
215 // then we really don't need the division since the denominator will always
216 // be 1 and we could just treat that curve as a quadratic.
217 // If w is (infinity/large enough),
218 // then the equation devolves into 2 straight lines P1->CP->P2, but
219 // the straightforward math may encounter infinity/NaN values in the
220 // intermediate stages. The limit as w approaches infinity are those
221 // two lines.
222 //
223 // Some examples: https://fiddle.skia.org/c/986b521feb3b832f04cbdfeefc9fbc58
224 // Note that the quadratic drawn in red in the center is identical to the
225 // conic with a weight of 1, drawn in green in the lower left.
227  // Start point.
229  // Control point.
231  // End point.
233 
234  // Weight
235  //
236  // We only need one value, but the underlying storage allocation is always
237  // a multiple of Point objects. To avoid confusion over which field the
238  // weight is stored in, and what the value of the other field may be, we
239  // store it in both x,y components of the |weight| field.
240  //
241  // This may also be an advantage eventually for code that can vectorize
242  // the conic calculations on both X & Y simultaneously.
244 
246 
248  : p1(ap1), cp(acp), p2(ap2), weight(weight, weight) {}
249 
250  Point Solve(Scalar time) const;
251 
252  void AppendPolylinePoints(Scalar scale_factor,
253  std::vector<Point>& points) const;
254 
255  using PointProc = std::function<void(const Point& point)>;
256 
257  void ToLinearPathComponents(Scalar scale_factor, const PointProc& proc) const;
258 
259  void ToLinearPathComponents(Scalar scale, VertexWriter& writer) const;
260 
261  size_t CountLinearPathComponents(Scalar scale) const;
262 
263  std::vector<Point> Extrema() const;
264 
265  bool operator==(const ConicPathComponent& other) const {
266  return p1 == other.p1 && cp == other.cp && p2 == other.p2 &&
267  weight == other.weight;
268  }
269 
270  std::optional<Vector2> GetStartDirection() const;
271 
272  std::optional<Vector2> GetEndDirection() const;
273 
274  std::array<QuadraticPathComponent, 2> ToQuadraticPathComponents() const;
275 
276  void SubdivideToQuadraticPoints(std::array<Point, 5>& points) const;
277 };
278 
279 // A component that represets a Cubic Bézier curve.
281  // Start point.
283  // The first control point.
285  // The second control point.
287  // End point.
289 
291 
293  : p1(q.p1),
294  cp1(q.p1 + (q.cp - q.p1) * (2.0 / 3.0)),
295  cp2(q.p2 + (q.cp - q.p2) * (2.0 / 3.0)),
296  p2(q.p2) {}
297 
298  CubicPathComponent(Point ap1, Point acp1, Point acp2, Point ap2)
299  : p1(ap1), cp1(acp1), cp2(acp2), p2(ap2) {}
300 
301  Point Solve(Scalar time) const;
302 
303  Point SolveDerivative(Scalar time) const;
304 
305  void AppendPolylinePoints(Scalar scale, std::vector<Point>& points) const;
306 
307  std::vector<Point> Extrema() const;
308 
309  using PointProc = std::function<void(const Point& point)>;
310 
311  void ToLinearPathComponents(Scalar scale, const PointProc& proc) const;
312 
313  void ToLinearPathComponents(Scalar scale, VertexWriter& writer) const;
314 
315  size_t CountLinearPathComponents(Scalar scale) const;
316 
318 
319  bool operator==(const CubicPathComponent& other) const {
320  return p1 == other.p1 && cp1 == other.cp1 && cp2 == other.cp2 &&
321  p2 == other.p2;
322  }
323 
324  std::optional<Vector2> GetStartDirection() const;
325 
326  std::optional<Vector2> GetEndDirection() const;
327 
328  private:
329  QuadraticPathComponent Lower() const;
330 };
331 
334 
335  // 0, 0 for closed, anything else for open.
336  Point closed = Point(1, 1);
337 
339 
340  constexpr bool IsClosed() const { return closed == Point{0, 0}; }
341 
343  : destination(p), closed(closed) {}
344 
345  bool operator==(const ContourComponent& other) const {
346  return destination == other.destination && IsClosed() == other.IsClosed();
347  }
348 };
349 
353 
354 } // namespace impeller
355 
356 #endif // FLUTTER_IMPELLER_GEOMETRY_PATH_COMPONENT_H_
A vertex writer that generates a triangle fan and requires primitive restart.
void EndContour() override
size_t GetIndexCount() const
void Write(Point point) override
FanVertexWriter(Point *point_buffer, uint16_t *index_buffer)
A vertex writer that has no hardware requirements.
GLESVertexWriter(std::vector< Point > &points, std::vector< uint16_t > &indices)
void Write(Point point) override
A vertex writer that generates a line strip topology.
std::pair< size_t, size_t > GetVertexCount() const
LineStripVertexWriter(std::vector< Point > &points)
void Write(Point point) override
const std::vector< Point > & GetOversizedBuffer() const
A vertex writer that generates a triangle strip and requires primitive restart.
void Write(Point point) override
StripVertexWriter(Point *point_buffer, uint16_t *index_buffer)
An interface for generating a multi contour polyline as a triangle strip.
virtual void EndContour()=0
virtual void Write(Point point)=0
int32_t value
float Scalar
Definition: scalar.h:18
TPoint< Scalar > Point
Definition: point.h:327
const Scalar scale
std::optional< Vector2 > GetEndDirection() const
std::array< QuadraticPathComponent, 2 > ToQuadraticPathComponents() const
size_t CountLinearPathComponents(Scalar scale) const
Point Solve(Scalar time) const
ConicPathComponent(Point ap1, Point acp, Point ap2, Scalar weight)
bool operator==(const ConicPathComponent &other) const
void ToLinearPathComponents(Scalar scale_factor, const PointProc &proc) const
std::vector< Point > Extrema() const
std::optional< Vector2 > GetStartDirection() const
void SubdivideToQuadraticPoints(std::array< Point, 5 > &points) const
std::function< void(const Point &point)> PointProc
void AppendPolylinePoints(Scalar scale_factor, std::vector< Point > &points) const
bool operator==(const ContourComponent &other) const
constexpr bool IsClosed() const
ContourComponent(Point p, Point closed)
void ToLinearPathComponents(Scalar scale, const PointProc &proc) const
size_t CountLinearPathComponents(Scalar scale) const
void AppendPolylinePoints(Scalar scale, std::vector< Point > &points) const
CubicPathComponent(Point ap1, Point acp1, Point acp2, Point ap2)
bool operator==(const CubicPathComponent &other) const
std::function< void(const Point &point)> PointProc
CubicPathComponent Subsegment(Scalar t0, Scalar t1) const
Point Solve(Scalar time) const
std::optional< Vector2 > GetStartDirection() const
std::vector< Point > Extrema() const
std::optional< Vector2 > GetEndDirection() const
Point SolveDerivative(Scalar time) const
CubicPathComponent(const QuadraticPathComponent &q)
std::optional< Vector2 > GetEndDirection() const
LinearPathComponent(Point ap1, Point ap2)
std::optional< Vector2 > GetStartDirection() const
std::vector< Point > Extrema() const
bool operator==(const LinearPathComponent &other) const
Point Solve(Scalar time) const
void AppendPolylinePoints(std::vector< Point > &points) const
size_t CountLinearPathComponents(Scalar scale) const
std::optional< Vector2 > GetEndDirection() const
QuadraticPathComponent(Point ap1, Point acp, Point ap2)
void AppendPolylinePoints(Scalar scale_factor, std::vector< Point > &points) const
bool operator==(const QuadraticPathComponent &other) const
std::function< void(const Point &point)> PointProc
std::vector< Point > Extrema() const
Point SolveDerivative(Scalar time) const
std::optional< Vector2 > GetStartDirection() const
void ToLinearPathComponents(Scalar scale_factor, const PointProc &proc) const
Point Solve(Scalar time) const