blob: 35932fc1df5ca1a61a6b86438da6e795e4124868 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/impeller/tessellator/circle_tessellator.h"
#include "flutter/fml/logging.h"
namespace impeller {
int CircleTessellator::kPrecomputedDivisions[kPrecomputedDivisionCount] = {
// clang-format off
1, 2, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 7,
8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10,
10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 13,
13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14,
15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18,
18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25,
25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27,
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28,
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
29, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33,
33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 36,
36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 38, 38, 38, 38,
38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 40, 40,
40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 41,
41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41,
41, 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43,
43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
43, 43, 43, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 44, 44, 44,
44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
44, 44, 44, 44, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
45, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49,
49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 51, 51, 51, 51, 51,
51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52,
52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52,
52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53,
53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55,
55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55,
55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 57,
// clang-format on
};
size_t CircleTessellator::ComputeQuadrantDivisions(Scalar pixel_radius) {
if (pixel_radius <= 0.0) {
return 1;
}
int radius_index = ceil(pixel_radius);
if (radius_index < kPrecomputedDivisionCount) {
return kPrecomputedDivisions[radius_index];
}
// For a circle with N divisions per quadrant, the maximum deviation of
// the polgyon approximation from the true circle will be at the center
// of the base of each triangular pie slice. We can compute that distance
// by finding the midpoint of the line of the first slice and compare
// its distance from the center of the circle to the radius. We will aim
// to have the length of that bisector to be within |kCircleTolerance|
// from the radius in pixels.
//
// Each vertex will appear at an angle of:
// theta(i) = (kPi / 2) * (i / N) // for i in [0..N]
// with each point falling at:
// point(i) = r * (cos(theta), sin(theta))
// If we consider the unit circle to simplify the calculations below then
// we need to scale the tolerance from its absolute quantity into a unit
// circle fraction:
// k = tolerance / radius
// Using this scaled tolerance below to avoid multiplying by the radius
// throughout all of the math, we have:
// first point = (1, 0) // theta(0) == 0
// theta = kPi / 2 / N // theta(1)
// second point = (cos(theta), sin(theta)) = (c, s)
// midpoint = (first + second) * 0.5 = ((1 + c)/2, s/2)
// |midpoint| = sqrt((1 + c)*(1 + c)/4 + s*s/4)
// = sqrt((1 + c + c + c*c + s*s) / 4)
// = sqrt((1 + 2c + 1) / 4)
// = sqrt((2 + 2c) / 4)
// = sqrt((1 + c) / 2)
// = cos(theta / 2) // using half-angle cosine formula
// error = 1 - |midpoint| = 1 - cos(theta / 2)
// cos(theta/2) = 1 - error
// theta/2 = acos(1 - error)
// kPi / 2 / N / 2 = acos(1 - error)
// kPi / 4 / acos(1 - error) = N
// Since we need error <= k, we want divisions >= N, so we use:
// N = ceil(kPi / 4 / acos(1 - k))
//
// Math is confirmed in https://math.stackexchange.com/a/4132095
// (keeping in mind that we are computing quarter circle divisions here)
// which also points out a performance optimization that is accurate
// to within an over-estimation of 1 division would be:
// N = ceil(kPi / 4 / sqrt(2 * k))
// Since we have precomputed the divisions for radii up to 1024, we can
// afford to be more accurate using the acos formula here for larger radii.
double k = kCircleTolerance / pixel_radius;
return ceil(kPiOver4 / std::acos(1 - k));
}
const std::vector<Trig>& CircleTessellator::GetTrigsForDivisions(
std::shared_ptr<Tessellator>& tessellator,
size_t divisions) {
std::vector<Trig>& trigs = (divisions < Tessellator::kCachedTrigCount)
? tessellator->precomputed_trigs_[divisions]
: temp_trigs_;
if (trigs.empty()) {
// Either not cached yet, or we are usig the temp vector...
trigs.reserve(divisions + 1);
double angle_scale = kPiOver2 / divisions;
trigs.emplace_back(1.0, 0.0);
for (size_t i = 1; i < divisions; i++) {
trigs.emplace_back(Radians(i * angle_scale));
}
trigs.emplace_back(0.0, 1.0);
FML_DCHECK(trigs.size() == divisions + 1);
}
return trigs;
}
void CircleTessellator::GenerateCircleTriangleStrip(
const TessellatedPointProc& proc,
const Point& center,
Scalar radius) const {
// Quadrant 1 connecting with Quadrant 4:
for (auto& trig : trigs_) {
auto offset = trig * radius;
proc({center.x - offset.x, center.y + offset.y});
proc({center.x - offset.x, center.y - offset.y});
}
// The second half of the circle should be iterated in reverse, but
// we can instead iterate forward and swap the x/y values of the
// offset as the angles should be symmetric and thus should generate
// symmetrically reversed trig vectors.
// Quadrant 2 connecting with Quadrant 2:
for (auto& trig : trigs_) {
auto offset = trig * radius;
proc({center.x + offset.y, center.y + offset.x});
proc({center.x + offset.y, center.y - offset.x});
}
}
void CircleTessellator::GenerateStrokedCircleTriangleStrip(
const TessellatedPointProc& proc,
const Point& center,
Scalar outer_radius,
Scalar inner_radius) const {
// Zig-zag back and forth between points on the outer circle and the
// inner circle. Both circles are evaluated at the same number of
// quadrant divisions so the points for a given division should match
// 1 for 1 other than their applied radius.
// Quadrant 1:
for (auto& trig : trigs_) {
auto outer = trig * outer_radius;
auto inner = trig * inner_radius;
proc({center.x - outer.x, center.y - outer.y});
proc({center.x - inner.x, center.y - inner.y});
}
// The even quadrants of the circle should be iterated in reverse, but
// we can instead iterate forward and swap the x/y values of the
// offset as the angles should be symmetric and thus should generate
// symmetrically reversed trig vectors.
// Quadrant 2:
for (auto& trig : trigs_) {
auto outer = trig * outer_radius;
auto inner = trig * inner_radius;
proc({center.x + outer.y, center.y - outer.x});
proc({center.x + inner.y, center.y - inner.x});
}
// Quadrant 3:
for (auto& trig : trigs_) {
auto outer = trig * outer_radius;
auto inner = trig * inner_radius;
proc({center.x + outer.x, center.y + outer.y});
proc({center.x + inner.x, center.y + inner.y});
}
// Quadrant 4:
for (auto& trig : trigs_) {
auto outer = trig * outer_radius;
auto inner = trig * inner_radius;
proc({center.x - outer.y, center.y + outer.x});
proc({center.x - inner.y, center.y + inner.x});
}
}
void CircleTessellator::GenerateRoundCapLineTriangleStrip(
const TessellatedPointProc& proc,
const Point& p0,
const Point& p1,
Scalar radius) const {
auto along = p1 - p0;
auto length = along.GetLength();
if (length < kEhCloseEnough) {
return GenerateCircleTriangleStrip(proc, p0, radius);
}
along *= radius / length;
auto across = Point(-along.y, along.x);
for (auto& trig : trigs_) {
auto relative_across = across * trig.cos;
auto relative_along = along * trig.sin;
proc({p0 + relative_across - relative_along});
proc({p1 + relative_across + relative_along});
}
// The second half of the round caps should be iterated in reverse, but
// we can instead iterate forward and swap the sin/cos values as they
// should be symmetric.
for (auto& trig : trigs_) {
auto relative_across = across * trig.sin;
auto relative_along = along * trig.cos;
proc({p0 - relative_across - relative_along});
proc({p1 - relative_across + relative_along});
}
}
} // namespace impeller