blob: c2a808cf18d2a6a74a171f956972337705ff9d67 [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.
#pragma once
#include <type_traits>
#include <variant>
#include <vector>
#include "impeller/geometry/point.h"
#include "impeller/geometry/rect.h"
#include "impeller/geometry/scalar.h"
namespace impeller {
// The default tolerance value for QuadraticCurveComponent::CreatePolyline and
// CubicCurveComponent::CreatePolyline. It also impacts the number of quadratics
// created when flattening a cubic curve to a polyline.
//
// Smaller numbers mean more points. This number seems suitable for particularly
// curvy curves at scales close to 1.0. As the scale increases, this number
// should be divided by Matrix::GetMaxBasisLength to avoid generating too few
// points for the given scale.
static constexpr Scalar kDefaultCurveTolerance = .1f;
struct LinearPathComponent {
Point p1;
Point p2;
LinearPathComponent() {}
LinearPathComponent(Point ap1, Point ap2) : p1(ap1), p2(ap2) {}
Point Solve(Scalar time) const;
std::vector<Point> CreatePolyline() const;
std::vector<Point> Extrema() const;
bool operator==(const LinearPathComponent& other) const {
return p1 == other.p1 && p2 == other.p2;
}
std::optional<Vector2> GetStartDirection() const;
std::optional<Vector2> GetEndDirection() const;
};
struct QuadraticPathComponent {
Point p1;
Point cp;
Point p2;
QuadraticPathComponent() {}
QuadraticPathComponent(Point ap1, Point acp, Point ap2)
: p1(ap1), cp(acp), p2(ap2) {}
Point Solve(Scalar time) const;
Point SolveDerivative(Scalar time) const;
// Uses the algorithm described by Raph Levien in
// https://raphlinus.github.io/graphics/curves/2019/12/23/flatten-quadbez.html.
//
// The algorithm has several benefits:
// - It does not require elevation to cubics for processing.
// - It generates fewer and more accurate points than recursive subdivision.
// - Each turn of the core iteration loop has no dependencies on other turns,
// making it trivially parallelizable.
//
// See also the implementation in kurbo: https://github.com/linebender/kurbo.
std::vector<Point> CreatePolyline(Scalar scale) const;
void FillPointsForPolyline(std::vector<Point>& points,
Scalar scale_factor) const;
std::vector<Point> Extrema() const;
bool operator==(const QuadraticPathComponent& other) const {
return p1 == other.p1 && cp == other.cp && p2 == other.p2;
}
std::optional<Vector2> GetStartDirection() const;
std::optional<Vector2> GetEndDirection() const;
};
struct CubicPathComponent {
Point p1;
Point cp1;
Point cp2;
Point p2;
CubicPathComponent() {}
CubicPathComponent(const QuadraticPathComponent& q)
: p1(q.p1),
cp1(q.p1 + (q.cp - q.p1) * (2.0 / 3.0)),
cp2(q.p2 + (q.cp - q.p2) * (2.0 / 3.0)),
p2(q.p2) {}
CubicPathComponent(Point ap1, Point acp1, Point acp2, Point ap2)
: p1(ap1), cp1(acp1), cp2(acp2), p2(ap2) {}
Point Solve(Scalar time) const;
Point SolveDerivative(Scalar time) const;
// This method approximates the cubic component with quadratics, and then
// generates a polyline from those quadratics.
//
// See the note on QuadraticPathComponent::CreatePolyline for references.
std::vector<Point> CreatePolyline(Scalar scale) const;
std::vector<Point> Extrema() const;
std::vector<QuadraticPathComponent> ToQuadraticPathComponents(
Scalar accuracy) const;
CubicPathComponent Subsegment(Scalar t0, Scalar t1) const;
bool operator==(const CubicPathComponent& other) const {
return p1 == other.p1 && cp1 == other.cp1 && cp2 == other.cp2 &&
p2 == other.p2;
}
std::optional<Vector2> GetStartDirection() const;
std::optional<Vector2> GetEndDirection() const;
private:
QuadraticPathComponent Lower() const;
};
struct ContourComponent {
Point destination;
bool is_closed = false;
ContourComponent() {}
ContourComponent(Point p, bool is_closed = false)
: destination(p), is_closed(is_closed) {}
bool operator==(const ContourComponent& other) const {
return destination == other.destination && is_closed == other.is_closed;
}
};
using PathComponentVariant = std::variant<std::monostate,
const LinearPathComponent*,
const QuadraticPathComponent*,
const CubicPathComponent*>;
struct PathComponentStartDirectionVisitor {
std::optional<Vector2> operator()(const LinearPathComponent* component);
std::optional<Vector2> operator()(const QuadraticPathComponent* component);
std::optional<Vector2> operator()(const CubicPathComponent* component);
std::optional<Vector2> operator()(std::monostate monostate) {
return std::nullopt;
}
};
struct PathComponentEndDirectionVisitor {
std::optional<Vector2> operator()(const LinearPathComponent* component);
std::optional<Vector2> operator()(const QuadraticPathComponent* component);
std::optional<Vector2> operator()(const CubicPathComponent* component);
std::optional<Vector2> operator()(std::monostate monostate) {
return std::nullopt;
}
};
static_assert(!std::is_polymorphic<LinearPathComponent>::value);
static_assert(!std::is_polymorphic<QuadraticPathComponent>::value);
static_assert(!std::is_polymorphic<CubicPathComponent>::value);
} // namespace impeller