18 class ConvexRearranger {
22 virtual ~ConvexRearranger() {}
24 virtual size_t ContourLength()
const = 0;
26 virtual Point GetPoint(
size_t i)
const = 0;
28 void RearrangeIntoTriangleStrip(
Point* output) {
29 size_t index_count = 0;
31 output[index_count++] = GetPoint(0);
34 size_t contour_length = ContourLength();
35 size_t b = contour_length - 1;
37 output[index_count++] = GetPoint(a);
38 output[index_count++] = GetPoint(
b);
43 output[index_count++] = GetPoint(
b);
48 ConvexRearranger(
const ConvexRearranger&) =
delete;
49 ConvexRearranger& operator=(
const ConvexRearranger&) =
delete;
57 class UnevenQuadrantsRearranger :
public ConvexRearranger {
59 UnevenQuadrantsRearranger(
Point* cache,
size_t segment_capacity)
60 : cache_(cache), segment_capacity_(segment_capacity) {}
62 Point* QuadCache(
size_t i) {
return cache_ + segment_capacity_ * i; }
64 const Point* QuadCache(
size_t i)
const {
65 return cache_ + segment_capacity_ * i;
68 size_t& QuadSize(
size_t i) {
return lengths_[i]; }
70 size_t ContourLength()
const override {
71 return lengths_[0] + lengths_[1] + lengths_[2] + lengths_[3] - 4;
74 Point GetPoint(
size_t i)
const override {
80 size_t high = lengths_[0] - 1;
82 return QuadCache(0)[i];
84 high += lengths_[1] - 1;
86 return QuadCache(1)[high - i];
89 high += lengths_[2] - 1;
91 return QuadCache(2)[i - low];
93 high += lengths_[3] - 1;
95 return QuadCache(3)[high - i];
104 size_t segment_capacity_;
114 class MirroredQuadrantRearranger :
public ConvexRearranger {
116 MirroredQuadrantRearranger(
Point center,
Point* cache)
117 : center_(center), cache_(cache) {}
119 size_t& QuadSize() {
return l_; }
121 size_t ContourLength()
const override {
return l_ * 4 - 4; }
123 Point GetPoint(
size_t i)
const override {
129 size_t high = l_ - 1;
131 return cache_[i] + center_;
135 return cache_[high - i] *
Point{1, -1} + center_;
140 return cache_[i - low] *
Point{-1, -1} + center_;
144 return cache_[high - i] *
Point{-1, 1} + center_;
159 constexpr Matrix kFlip = Matrix(
160 0.0f, 1.0f, 0.0f, 0.0f,
161 1.0f, 0.0f, 0.0f, 0.0f,
162 0.0f, 0.0f, 1.0f, 0.0f,
163 0.0f, 0.0f, 0.0f, 1.0f);
176 constexpr
Scalar kPrecomputedVariables[][4] = {
177 {2.000, 2.00000, 0.00000, 0.24040},
178 {2.020, 2.03340, 0.01447, 0.24040},
179 {2.040, 2.06540, 0.02575, 0.21167},
180 {2.060, 2.09800, 0.03668, 0.20118},
181 {2.080, 2.13160, 0.04719, 0.19367},
182 {2.100, 2.17840, 0.05603, 0.16233},
183 {2.120, 2.19310, 0.06816, 0.20020},
184 {2.140, 2.22990, 0.07746, 0.19131},
185 {2.160, 2.26360, 0.08693, 0.19008},
186 {2.180, 2.30540, 0.09536, 0.17935},
187 {2.200, 2.32900, 0.10541, 0.19136},
188 {2.220, 2.38330, 0.11237, 0.17130},
189 {2.240, 2.39770, 0.12271, 0.18956},
190 {2.260, 2.41770, 0.13251, 0.20254},
191 {2.280, 2.47180, 0.13879, 0.18454},
192 {2.300, 2.50910, 0.14658, 0.18261}
195 constexpr
size_t kNumRecords =
196 sizeof(kPrecomputedVariables) /
sizeof(kPrecomputedVariables[0]);
197 constexpr
Scalar kMinRatio = kPrecomputedVariables[0][0];
198 constexpr
Scalar kMaxRatio = kPrecomputedVariables[kNumRecords - 1][0];
199 constexpr
Scalar kRatioStep =
200 kPrecomputedVariables[1][0] - kPrecomputedVariables[0][0];
208 Scalar LerpPrecomputedVariable(
size_t column,
Scalar ratio) {
210 std::clamp<Scalar>((ratio - kMinRatio) / kRatioStep, 0, kNumRecords - 1);
211 size_t left = std::clamp<size_t>(
static_cast<size_t>(std::floor(steps)), 0,
213 Scalar frac = steps - left;
215 return (1 - frac) * kPrecomputedVariables[left][column] +
216 frac * kPrecomputedVariables[left + 1][column];
223 constexpr
Scalar kMaxQuadrantSteps = 40;
240 constexpr
Scalar pointsPerPixel = 1.0;
241 size_t pointsByDimension =
242 static_cast<size_t>(std::ceil(minDimension * pointsPerPixel));
243 Scalar angleByDimension = fullAngle / pointsByDimension;
245 return std::min(kMinAngleStep, angleByDimension);
261 constexpr
Scalar kGapFactor = 0.2924066406;
269 return (left * ratio_right + right * ratio_left) / (ratio_left + ratio_right);
283 size_t DrawCircularArc(
Point* output,
301 Point s_to_e = end - start;
302 Point m = (start + end) / 2;
304 Scalar distance_sm = s_to_e.GetLength() / 2;
305 Scalar distance_cm = sqrt(r * r - distance_sm * distance_sm);
307 Scalar angle_sce = asinf(distance_sm / r) * 2;
308 Point c_to_s = start - c;
311 Point* next = output;
312 Scalar angle = reverse ? angle_sce : 0.0f;
314 (reverse ? -1 : 1) * CalculateStep(std::abs(s_to_e.y), angle_sce);
315 Scalar end_angle = reverse ? 0.0f : angle_sce;
317 while ((angle < end_angle) != reverse) {
318 *(next++) = full_transform * c_to_s.Rotate(Radians(-angle));
321 return next - output;
335 size_t DrawSuperellipsoidArc(
Point* output,
341 Point* next = output;
342 Scalar angle = reverse ? max_theta : 0.0f;
345 CalculateStep(a - a * pow(abs(cosf(max_theta)), 2 / n), max_theta);
346 Scalar end = reverse ? 0.0f : max_theta;
347 while ((angle < end) != reverse) {
348 Scalar x = a * pow(abs(sinf(angle)), 2 / n);
349 Scalar y = a * pow(abs(cosf(angle)), 2 / n);
353 return next - output;
370 size_t DrawOctantSquareLikeSquircle(
Point* output,
407 Scalar ratio = {std::min(size / corner_radius, kMaxRatio)};
408 Scalar a = ratio * corner_radius / 2;
410 Scalar g = kGapFactor * corner_radius;
412 Scalar n = LerpPrecomputedVariable(1, ratio);
413 Scalar d = LerpPrecomputedVariable(2, ratio) * a;
414 Scalar thetaJ = LerpPrecomputedVariable(3, ratio);
416 Scalar R = (a - d - g) * sqrt(2);
418 Point pointA{0, size / 2};
419 Point pointM{size / 2 - g, size / 2 - g};
422 Point{pow(abs(sinf(thetaJ)), 2 / n), pow(abs(cosf(thetaJ)), 2 / n)} * a +
426 Point* next = output;
431 next += DrawSuperellipsoidArc(next, a, n, thetaJ, reverse,
434 next += DrawCircularArc(next, pointJ, pointM, R, reverse,
transform);
437 next += DrawCircularArc(next, pointJ, pointM, R, reverse,
transform);
439 next += DrawSuperellipsoidArc(next, a, n, thetaJ, reverse,
446 return next - output;
455 static size_t DrawQuadrant(
Point* output,
459 if (radii.width == 0 || radii.height == 0) {
461 output[0] = {center.x, outer.y};
463 output[2] = {outer.x, center.y};
470 Scalar norm_radius = radii.MinDimension();
471 Size radius_scale = radii / norm_radius;
472 Point signed_size = (outer - center) * 2;
473 Point norm_size = signed_size.
Abs() / radius_scale;
474 Point signed_scale = signed_size / norm_size;
480 Scalar c = (norm_size.x - norm_size.y) / 2;
482 Point* next = output;
484 next += DrawOctantSquareLikeSquircle(
485 next, norm_size.x, norm_radius,
false,
489 next += DrawOctantSquareLikeSquircle(
490 next, norm_size.y, norm_radius,
true,
494 return next - output;
501 : bounds_(bounds.GetPositive()), radii_(radii.Scaled(bounds_)) {}
521 static_assert(kMaxQuadSize > 2 * kMaxQuadrantSteps);
523 ConvexRearranger* rearranger;
524 std::variant<std::monostate, MirroredQuadrantRearranger,
525 UnevenQuadrantsRearranger>
529 rearranger_holder.emplace<MirroredQuadrantRearranger>(bounds_.
GetCenter(),
531 auto& t = std::get<MirroredQuadrantRearranger>(rearranger_holder);
535 t.QuadSize() = DrawQuadrant(cache,
Point(),
539 rearranger_holder.emplace<UnevenQuadrantsRearranger>(cache, kMaxQuadSize);
540 auto& t = std::get<UnevenQuadrantsRearranger>(rearranger_holder);
555 t.QuadSize(0) = DrawQuadrant(t.QuadCache(0),
Point{top_split, right_split},
558 DrawQuadrant(t.QuadCache(1),
Point{bottom_split, right_split},
561 DrawQuadrant(t.QuadCache(2),
Point{bottom_split, left_split},
563 t.QuadSize(3) = DrawQuadrant(t.QuadCache(3),
Point{top_split, left_split},
567 size_t contour_length = rearranger->ContourLength();
569 nullptr,
sizeof(
Point) * contour_length,
alignof(
Point));
571 reinterpret_cast<Point*
>(vertex_buffer.GetBuffer()->OnGetContents() +
572 vertex_buffer.GetRange().offset);
573 rearranger->RearrangeIntoTriangleStrip(vertex_data);
575 return GeometryResult{
579 .vertex_buffer = vertex_buffer,
580 .vertex_count = contour_length,
587 std::optional<Rect> RoundSuperellipseGeometry::GetCoverage(
593 const Rect& rect)
const {
594 if (!
transform.IsTranslationScaleOnly()) {
605 bounds_.
GetTop() + top_inset * kGapFactor,
606 bounds_.
GetRight() - right_inset * kGapFactor,
607 bounds_.
GetBottom() - bottom_inset * kGapFactor);
HostBuffer & GetTransientsBuffer() const
Retrieve the currnent host buffer for transient storage.
Tessellator & GetTessellator() const
Matrix GetShaderTransform(const RenderPass &pass) const
Get the vertex shader transform used for drawing this Entity.
BufferView Emplace(const BufferType &buffer, size_t alignment=0)
Emplace non-uniform data (like contiguous vertices) onto the host buffer.
Render passes encode render commands directed as one specific render target into an underlying comman...
~RoundSuperellipseGeometry() override
bool CoversArea(const Matrix &transform, const Rect &rect) const override
Determines if this geometry, transformed by the given transform, will completely cover all surface ar...
bool IsAxisAlignedRect() const override
RoundSuperellipseGeometry(const Rect &bounds, const RoundingRadii &radii)
std::vector< Point > & GetStrokePointCache()
Retrieve a pre-allocated arena of kPointArenaSize points.
@ kNone
Does not use the index buffer.
static constexpr size_t kPointArenaSize
The size of the point arena buffer stored on the tessellator.
A 4x4 matrix using column-major storage.
static constexpr Matrix MakeTranslation(const Vector3 &t)
static constexpr Matrix MakeTranslateScale(const Vector3 &s, const Vector3 &t)
constexpr bool AreAllCornersSame(Scalar tolerance=kEhCloseEnough) const
constexpr TPoint Abs() const
constexpr TPoint Normalize() const
constexpr auto GetBottom() const
constexpr TRect TransformBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle.
constexpr auto GetTop() const
constexpr bool Contains(const TPoint< Type > &p) const
Returns true iff the provided point |p| is inside the half-open interior of this rectangle.
constexpr auto GetLeft() const
constexpr auto GetRight() const
constexpr TPoint< T > GetLeftBottom() const
constexpr TPoint< T > GetRightTop() const
constexpr TPoint< T > GetRightBottom() const
constexpr Point GetCenter() const
Get the center point as a |Point|.
constexpr TPoint< T > GetLeftTop() const
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)