Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 4dbaae2

Browse files
committed
switch to triangle strips for circles and simplify tessellation API
1 parent fbe91b4 commit 4dbaae2

File tree

7 files changed

+252
-289
lines changed

7 files changed

+252
-289
lines changed

impeller/entity/geometry/circle_tessellator.cc

Lines changed: 44 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -52,79 +52,57 @@ const std::vector<Trig>& CircleTessellator::GetTrigForDivisions(
5252
return trigs;
5353
}
5454

55-
void CircleTessellator::ExtendRelativeQuadrantToAbsoluteCircle(
56-
std::vector<Point>& points,
57-
const Point& center) {
58-
auto quadrant_points = points.size();
59-
60-
// The 1st quadrant points are reversed in order, reflected around
61-
// the Y axis, and translated to become absolute 2nd quadrant points.
62-
for (size_t i = 1; i <= quadrant_points; i++) {
63-
auto point = points[quadrant_points - i];
64-
points.emplace_back(center.x + point.x, center.y - point.y);
65-
}
55+
void CircleTessellator::GenerateCircleTriangleStrip(
56+
const TessellatedPointProc& proc,
57+
const Point& center,
58+
Scalar radius) const {
59+
auto trigs = GetTrigForDivisions(quadrant_divisions_);
6660

67-
// The 1st quadrant points are reflected around the X & Y axes
68-
// and translated to become absolute 3rd quadrant points.
69-
for (size_t i = 0; i < quadrant_points; i++) {
70-
auto point = points[i];
71-
points.emplace_back(center.x - point.x, center.y - point.y);
61+
for (auto& trig : trigs) {
62+
auto offset = trig * radius;
63+
proc({center.x - offset.x, center.y + offset.y});
64+
proc({center.x - offset.x, center.y - offset.y});
7265
}
73-
74-
// The 1st quadrant points are reversed in order, reflected around
75-
// the X axis and translated to become absolute 4th quadrant points.
76-
// The 1st quadrant points are also translated to the center point as
77-
// well since this is the last time we will use them.
78-
for (size_t i = 1; i <= quadrant_points; i++) {
79-
auto point = points[quadrant_points - i];
80-
points.emplace_back(center.x - point.x, center.y + point.y);
81-
82-
// This is the last loop where we need the first quadrant to be
83-
// relative so we convert them to absolute as we go.
84-
points[quadrant_points - i] = center + point;
66+
// The second half of the circle should be iterated in reverse, but
67+
// we can instead iterate forward and swap the x/y values of the
68+
// offset as the angles should be symmetric and thus should generate
69+
// symmetrically reversed offset vectors.
70+
for (auto& trig : trigs) {
71+
auto offset = trig * radius;
72+
proc({center.x + offset.y, center.y + offset.x});
73+
proc({center.x + offset.y, center.y - offset.x});
8574
}
8675
}
8776

88-
void CircleTessellator::FillQuadrantTriangles(std::vector<Point>& points,
89-
const Point& center,
90-
const Point& start_vector,
91-
const Point& end_vector) const {
92-
// We only deal with circles for now
93-
FML_DCHECK(start_vector.GetLength() - end_vector.GetLength() <
94-
kEhCloseEnough);
95-
// And only for perpendicular vectors
96-
FML_DCHECK(start_vector.Dot(end_vector) < kEhCloseEnough);
97-
77+
void CircleTessellator::GenerateRoundCapLineTriangleStrip(
78+
const TessellatedPointProc& proc,
79+
const Point& p0,
80+
const Point& p1,
81+
Scalar radius) const {
9882
auto trigs = GetTrigForDivisions(quadrant_divisions_);
99-
100-
auto prev = center + (trigs[0].cos * start_vector + //
101-
trigs[0].sin * end_vector);
102-
for (size_t i = 1; i < trigs.size(); i++) {
103-
points.emplace_back(center);
104-
points.emplace_back(prev);
105-
prev = center + (trigs[i].cos * start_vector + //
106-
trigs[i].sin * end_vector);
107-
points.emplace_back(prev);
83+
auto along = p1 - p0;
84+
auto length = along.GetLength();
85+
if (length < kEhCloseEnough) {
86+
return GenerateCircleTriangleStrip(proc, p0, radius);
87+
}
88+
along *= radius / length;
89+
auto across = Point(-along.y, along.x);
90+
91+
for (auto& trig : trigs) {
92+
auto relative_across = across * trig.cos;
93+
auto relative_along = along * trig.sin;
94+
proc({p0 + relative_across - relative_along});
95+
proc({p1 + relative_across + relative_along});
96+
}
97+
// The second half of the round caps should be iterated in reverse, but
98+
// we can instead iterate forward and swap the sin/cos values as they
99+
// should be symmetric.
100+
for (auto& trig : trigs) {
101+
auto relative_across = across * trig.sin;
102+
auto relative_along = along * trig.cos;
103+
proc({p0 - relative_across - relative_along});
104+
proc({p1 - relative_across + relative_along});
108105
}
109-
}
110-
111-
std::vector<Point> CircleTessellator::GetCircleTriangles(const Point& center,
112-
Scalar radius) const {
113-
std::vector<Point> points = std::vector<Point>();
114-
const size_t quadrant_points = quadrant_divisions_ * 3;
115-
points.reserve(quadrant_points * 4);
116-
117-
// Start with the quadrant top-center to right-center using coordinates
118-
// relative to the (0, 0). The coordinates will be made absolute relative
119-
// to the center during the extend method below.
120-
FillQuadrantTriangles(points, {}, {0, -radius}, {radius, 0});
121-
FML_DCHECK(points.size() == quadrant_points);
122-
123-
ExtendRelativeQuadrantToAbsoluteCircle(points, center);
124-
125-
FML_DCHECK(points.size() == quadrant_points * 4);
126-
127-
return points;
128106
}
129107

130108
} // namespace impeller

impeller/entity/geometry/circle_tessellator.h

Lines changed: 53 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#pragma once
66

7+
#include <functional>
78
#include <vector>
89

910
#include "flutter/impeller/geometry/matrix.h"
@@ -22,33 +23,37 @@ struct Trig {
2223

2324
double cos;
2425
double sin;
26+
27+
Vector2 operator*(Scalar radius) const {
28+
return Vector2(cos * radius, sin * radius);
29+
}
30+
31+
Vector2 interpolate(Vector2 start_vector, Vector2 end_vector) {
32+
return start_vector * cos + end_vector * sin;
33+
}
2534
};
2635

36+
using TessellatedPointProc = std::function<void(const Point& p)>;
37+
2738
/// @brief A utility class to compute the number of divisions for a circle
2839
/// given a transform-adjusted pixel radius and methods for generating
2940
/// a tessellated set of triangles for a quarter or full circle.
3041
///
31-
/// A helper constructor is provided which can compute the device
32-
/// pixel radius size for a geometry-space radius when viewed under
33-
/// a specified geometry-to-device transform.
42+
/// The constructor will compute the device pixel radius size for
43+
/// the specified geometry-space |radius| when viewed under
44+
/// a specified geometry-to-device |transform|.
3445
///
35-
/// The object should be constructed with the expected radius of the
36-
/// circle in pixels, but can then be used to generate a triangular
37-
/// tessellation with the indicated number of divisions for any
46+
/// The object should be constructed with the expected transform and
47+
/// radius of the circle, but can then be used to generate a triangular
48+
/// tessellation with the computed number of divisions for any
3849
/// radius after that. Since the coordinate space in which the
3950
/// circle being tessellated is not necessarily device pixel space,
4051
/// the radius supplied during tessellation might not match the
41-
/// pixel radius supplied during construction, but the two values
52+
/// pixel radius computed during construction, but the two values
4253
/// should be related by the transform in place when the tessellated
4354
/// triangles are rendered for maximum tessellation fidelity.
4455
class CircleTessellator {
4556
public:
46-
/// @brief Constructs a CircleDivider that produces enough segments to
47-
/// reasonably approximate a circle with a specified radius
48-
/// in pixels.
49-
constexpr explicit CircleTessellator(Scalar pixel_radius)
50-
: quadrant_divisions_(ComputeQuadrantDivisions(pixel_radius)) {}
51-
5257
/// @brief Constructs a CircleDivider that produces enough segments to
5358
/// reasonably approximate a circle with a specified |radius|
5459
/// when viewed under the specified |transform|.
@@ -62,40 +67,47 @@ class CircleTessellator {
6267
size_t GetQuadrantDivisionCount() const { return quadrant_divisions_; }
6368

6469
/// @brief Return the number of vertices that will be generated to
65-
/// tessellate a single quarter circle.
66-
size_t GetQuadrantVertexCount() const { return quadrant_divisions_ * 3; }
67-
68-
/// @brief Return the number of divisions computed by the algorithm for
69-
/// a full circle.
70-
size_t GetCircleDivisionCount() const { return quadrant_divisions_ * 4; }
71-
72-
/// @brief Return the number of vertices that will be generated to
73-
/// tessellate a full circle.
74-
size_t GetCircleVertexCount() const { return quadrant_divisions_ * 12; }
75-
76-
/// @brief Compute the points of a triangular tesselation of the full
77-
/// circle of the given radius and center.
70+
/// tessellate a full circle with a triangle strip.
71+
///
72+
/// This value can be used to pre-allocate space in a vector
73+
/// to hold the vertices that will be produced by the
74+
/// |GenerateCircleTriangleStrip| and
75+
/// |GenerateRoundCapLineTriangleStrip| methods.
76+
size_t GetCircleVertexCount() const { return (quadrant_divisions_ + 1) * 4; }
77+
78+
/// @brief Generate the vertices for a triangle strip that covers the
79+
/// circle at a given |radius| from a given |center|, delivering
80+
/// the computed coordinates to the supplied |proc|.
7881
///
79-
/// @return the list of points on the polygonal approximation.
80-
std::vector<Point> GetCircleTriangles(const Point& center,
81-
Scalar radius) const;
82-
83-
/// @brief Adds entries in an existing vector of |vertices| to represent
84-
/// the triangular tessellation of the quarter circle that sweeps
85-
/// from the |start_vector| to the |end_vector| around an origin
86-
/// specified by |center|. The length of the start and end vectors
87-
/// controls the size of the quarter circle.
82+
/// This procedure will generate no more than the number of
83+
/// vertices returned by |GetCircleVertexCount| in an order
84+
/// appropriate for rendering as a triangle strip.
85+
void GenerateCircleTriangleStrip(const TessellatedPointProc& proc,
86+
const Point& center,
87+
Scalar radius) const;
88+
89+
/// @brief Generate the vertices for a triangle strip that covers the
90+
/// line from |p0| to |p1| with round caps of the specified
91+
/// |radius|, delivering the computed coordinates to the supplied
92+
/// |proc|.
8893
///
89-
/// The axes must be of the same length and perpendicular. The new
90-
/// points will be appended to the end of the vector.
91-
void FillQuadrantTriangles(std::vector<Point>& vertices,
92-
const Point& center,
93-
const Point& start_vector,
94-
const Point& end_vector) const;
94+
/// This procedure will generate no more than the number of
95+
/// vertices returned by |GetCircleVertexCount| in an order
96+
/// appropriate for rendering as a triangle strip.
97+
void GenerateRoundCapLineTriangleStrip(const TessellatedPointProc& proc,
98+
const Point& p0,
99+
const Point& p1,
100+
Scalar radius) const;
95101

96102
private:
97103
const size_t quadrant_divisions_;
98104

105+
/// @brief Constructs a CircleDivider that produces enough segments to
106+
/// reasonably approximate a circle with a specified radius
107+
/// in pixels.
108+
constexpr explicit CircleTessellator(Scalar pixel_radius)
109+
: quadrant_divisions_(ComputeQuadrantDivisions(pixel_radius)) {}
110+
99111
CircleTessellator(const CircleTessellator&) = delete;
100112

101113
CircleTessellator& operator=(const CircleTessellator&) = delete;
@@ -116,20 +128,6 @@ class CircleTessellator {
116128
/// @return The vector of (divisions + 1) trig values.
117129
static const std::vector<Trig>& GetTrigForDivisions(size_t divisions);
118130

119-
/// @brief Extend a list of |points| in the vector containing relative
120-
/// coordinates for the first quadrant (top-center to right-center)
121-
/// into the remaining 3 quadrants and adjust them to be relative
122-
/// to the supplied |center|.
123-
///
124-
/// The incoming coordinates are assumed to be relative to a
125-
/// center point of (0, 0) and the method will duplicate and
126-
/// reflect them around that origin to fill in the remaining
127-
/// 3 quadrants. As the method works, it will also adjust every
128-
/// point (including the pre-existing 1st quadrant points) to
129-
/// be relative to the new center.
130-
static void ExtendRelativeQuadrantToAbsoluteCircle(std::vector<Point>& points,
131-
const Point& center = {});
132-
133131
static constexpr int MAX_DIVISIONS_ = 35;
134132

135133
static std::vector<Trig> trigs_[MAX_DIVISIONS_ + 1];

0 commit comments

Comments
 (0)