blob: 74624eb78a8c1e37532ced3145db82945ecd5bee [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.
#pragma once
#include <functional>
#include <vector>
#include "flutter/impeller/geometry/matrix.h"
#include "flutter/impeller/geometry/point.h"
#include "flutter/impeller/geometry/scalar.h"
#include "flutter/impeller/geometry/trig.h"
#include "flutter/impeller/tessellator/tessellator.h"
namespace impeller {
using TessellatedPointProc = std::function<void(const Point& p)>;
/// @brief A utility class to compute the number of divisions for a circle
/// given a transform-adjusted pixel radius and methods for generating
/// a tessellated set of triangles for a quarter or full circle.
///
/// The constructor will compute the device pixel radius size for
/// the specified geometry-space |radius| when viewed under
/// a specified geometry-to-device |transform|.
///
/// The object should be constructed with the expected transform and
/// radius of the circle, but can then be used to generate a triangular
/// tessellation with the computed number of divisions for any
/// radius after that. Since the coordinate space in which the
/// circle being tessellated is not necessarily device pixel space,
/// the radius supplied during tessellation might not match the
/// pixel radius computed during construction, but the two values
/// should be related by the transform in place when the tessellated
/// triangles are rendered for maximum tessellation fidelity.
class CircleTessellator {
public:
/// @brief The pixel tolerance used by the algorighm to determine how
/// many divisions to create for a circle.
///
/// No point on the polygon of vertices should deviate from the
/// true circle by more than this tolerance.
static constexpr Scalar kCircleTolerance = 0.1;
/// @brief Constructs a CircleTessellator that produces enough segments
/// to reasonably approximate a circle with a specified |radius|
/// when viewed under the specified |transform|.
CircleTessellator(std::shared_ptr<Tessellator>& tessellator,
const Matrix& transform,
Scalar radius)
: CircleTessellator(tessellator, transform.GetMaxBasisLength() * radius) {
}
~CircleTessellator() = default;
/// @brief Return the number of divisions computed by the algorithm for
/// a single quarter circle.
size_t GetQuadrantDivisionCount() const { return trigs_.size() - 1; }
/// @brief Return the number of vertices that will be generated to
/// tessellate a full circle with a triangle strip.
///
/// This value can be used to pre-allocate space in a vector
/// to hold the vertices that will be produced by the
/// |GenerateCircleTriangleStrip| and
/// |GenerateRoundCapLineTriangleStrip| methods.
size_t GetCircleVertexCount() const { return trigs_.size() * 4; }
/// @brief Generate the vertices for a triangle strip that covers the
/// circle at a given |radius| from a given |center|, delivering
/// the computed coordinates to the supplied |proc|.
///
/// This procedure will generate no more than the number of
/// vertices returned by |GetCircleVertexCount| in an order
/// appropriate for rendering as a triangle strip.
void GenerateCircleTriangleStrip(const TessellatedPointProc& proc,
const Point& center,
Scalar radius) const;
/// @brief Generate the vertices for a triangle strip that covers the
/// line from |p0| to |p1| with round caps of the specified
/// |radius|, delivering the computed coordinates to the supplied
/// |proc|.
///
/// This procedure will generate no more than the number of
/// vertices returned by |GetCircleVertexCount| in an order
/// appropriate for rendering as a triangle strip.
void GenerateRoundCapLineTriangleStrip(const TessellatedPointProc& proc,
const Point& p0,
const Point& p1,
Scalar radius) const;
private:
const std::vector<Trig>& trigs_;
std::vector<Trig> temp_trigs_;
/// @brief Constructs a CircleTessellator that produces enough segments
/// to reasonably approximate a circle with a specified radius
/// in pixels.
explicit CircleTessellator(std::shared_ptr<Tessellator>& tessellator,
Scalar pixel_radius)
: trigs_(GetTrigsForDivisions(tessellator,
ComputeQuadrantDivisions(pixel_radius))) {}
CircleTessellator(const CircleTessellator&) = delete;
CircleTessellator& operator=(const CircleTessellator&) = delete;
/// @brief Compute the number of vertices to divide each quadrant of
/// the circle into based on the expected pixel space radius.
///
/// @return the number of vertices.
static size_t ComputeQuadrantDivisions(Scalar pixel_radius);
/// @brief Compute the sine and cosine for each angle in the number of
/// divisions [0, divisions] of a quarter circle and return the
/// values in a vector of trig objects.
///
/// Note that since the 0th division is included, the vector will
/// contain (divisions + 1) values.
///
/// @return The vector of (divisions + 1) trig values.
const std::vector<Trig>& GetTrigsForDivisions(
std::shared_ptr<Tessellator>& tessellator,
size_t divisions);
static constexpr int kPrecomputedDivisionCount = 1024;
static int kPrecomputedDivisions[kPrecomputedDivisionCount];
};
} // namespace impeller