blob: 7416c8ce57269ad49253ddcdb1d9b22fee37e148 [file] [log] [blame] [edit]
// 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.
#ifndef FLUTTER_IMPELLER_GEOMETRY_ARC_H_
#define FLUTTER_IMPELLER_GEOMETRY_ARC_H_
#include "flutter/impeller/geometry/rect.h"
#include "flutter/impeller/geometry/scalar.h"
namespace impeller {
struct Arc {
/// A structure to describe the iteration through a set of angle vectors
/// in a |Trigs| structure to render the points along an arc. The start
/// and end vectors and each iteration's axis vector are all unit vectors
/// that point in the direction of the point on the circle to be emitted.
///
/// Each vector should be rendered by multiplying it by the radius of the
/// circle, or in the case of a stroked arc, by the inner and outer radii
/// of the sides of the stroke.
///
/// - The start vector will always be rendered first.
/// - Then each quadrant will be iterated by composing the trigs vectors
/// with the given axis vector, iterating from the start index (inclusive)
/// to the end index (exclusive) of the vector of |Trig| values.
/// - Finally the end vector will be rendered.
/// For example:
/// Insert(arc_iteration.start * radius);
/// for (size_t i = 0u; i < arc_iteration.quadrant_count; i++) {
/// Quadrant quadrant = arc_iteration.quadrants[i];
/// for (j = quadrant.start_index; j < quadrant.end_index; j++) {
/// Insert(trigs[j] * quadrant.axis * radius);
/// }
/// }
/// Insert(arc_iteration.end * radius);
///
/// The rendering routine may adjust the manner/order in which those vertices
/// are inserted into the vertex buffer to optimally match the vertex triangle
/// mode it plans to use, but the description above represents the basic
/// technique to compute the points along the actual curve.
struct Iteration {
// The axis to multiply by each |Trig| value and the half-open [start, end)
// range of indices into the associated |Trig| vector over which to compute.
struct Quadrant {
impeller::Vector2 axis;
size_t start_index = 0u;
size_t end_index = 0u;
size_t GetPointCount() const {
FML_DCHECK(start_index < end_index);
return end_index - start_index;
}
};
// The true begin and end angles of the arc, expressed as unit direction
// vectors.
impeller::Vector2 start;
impeller::Vector2 end;
// The variable number of quadrants that have to be iterated and
// cross-referenced with values in a |Trigs| object.
size_t quadrant_count = 0u;
// Normally, we have at most 5 |Quadrant| entries when an arc starts
// and ends in the same quadrant with the start angle later in the
// quadrant than the end angle.
//
// Worst case:
// - First iteration goes from the start angle to the end of that quadrant.
// - Then 3 full iterations for the 3 other full quarter circles.
// - Then a last iteration that goes from the start of that quadrant to the
// end angle.
//
// However, when we have an arc that sweeps past a full circle, then we
// can have up to 9 |Quadrant| entries. The extra quadrants are only
// interesting in the case where the arc is stroked and we are including
// the center. Such an arc should look like a complete circle with an
// additional pie sliced cut into it, but not removed. Expressing that
// case with one continuous path may require up to 7 full quadrants and
// 2 partial quadrants for 9 total quadrants in this degenerate stroking
// case.
//
// We can also have 0 quadrants for arcs that are smaller than the
// step size of the pixel-radius |Trigs| vector.
Quadrant quadrants[9];
size_t GetPointCount() const;
};
Arc(const Rect& bounds, Degrees start, Degrees sweep, bool include_center);
/// Return the bounds of the oval in which this arc is inscribed.
const Rect& GetOvalBounds() const { return bounds_; }
/// Returns the center of the oval bounds.
const Point GetOvalCenter() const { return bounds_.GetCenter(); }
/// Returns the size of the oval bounds.
const Size GetOvalSize() const { return bounds_.GetSize(); }
/// Return the tight bounds of the arc taking into account its specific
/// geometry such as the start and end angles and the center (if included).
Rect GetTightArcBounds() const;
constexpr Degrees GetStart() const { return start_; }
constexpr Degrees GetSweep() const { return sweep_; }
constexpr bool IncludeCenter() const { return include_center_; }
constexpr bool IsPerfectCircle() const { return bounds_.IsSquare(); }
constexpr bool IsFullCircle() const { return sweep_.degrees >= 360.0f; }
/// Return an |ArcIteration| that explains how to generate vertices for
/// the arc with the indicated number of steps in each full quadrant.
/// The step_count is typically chosen based on the size of the bounds
/// and the scale at which the arc is being drawn and so the computation
/// of the step_count requirements is left to the caller.
///
/// If the sweep is more than 360 degrees then the code may simplify
/// the iteration to a simple circle, but only if the simplify_360
/// parameter is true.
Iteration ComputeIterations(size_t step_count,
bool simplify_360 = true) const;
private:
Rect bounds_;
Degrees start_;
Degrees sweep_;
bool include_center_;
static const Iteration ComputeCircleArcIterations(size_t step_count);
};
} // namespace impeller
namespace std {
inline std::ostream& operator<<(std::ostream& out, const impeller::Arc& a) {
out << "Arc(" << a.GetOvalBounds() << ", " << a.GetStart() << " + "
<< a.GetSweep()
<< (a.IncludeCenter() ? ", with center)" : ", without center)");
return out;
}
} // namespace std
#endif // FLUTTER_IMPELLER_GEOMETRY_ARC_H_