diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index b650e4d16999a..2ec9a5af87ed5 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -42862,6 +42862,8 @@ ORIGIN: ../../../flutter/impeller/entity/geometry/round_rect_geometry.cc + ../.. ORIGIN: ../../../flutter/impeller/entity/geometry/round_rect_geometry.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/geometry/stroke_path_geometry.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/geometry/stroke_path_geometry.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/entity/geometry/superellipse_geometry.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/entity/geometry/superellipse_geometry.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/geometry/vertices_geometry.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/geometry/vertices_geometry.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/inline_pass_context.cc + ../../../flutter/LICENSE @@ -45739,6 +45741,8 @@ FILE: ../../../flutter/impeller/entity/geometry/round_rect_geometry.cc FILE: ../../../flutter/impeller/entity/geometry/round_rect_geometry.h FILE: ../../../flutter/impeller/entity/geometry/stroke_path_geometry.cc FILE: ../../../flutter/impeller/entity/geometry/stroke_path_geometry.h +FILE: ../../../flutter/impeller/entity/geometry/superellipse_geometry.cc +FILE: ../../../flutter/impeller/entity/geometry/superellipse_geometry.h FILE: ../../../flutter/impeller/entity/geometry/vertices_geometry.cc FILE: ../../../flutter/impeller/entity/geometry/vertices_geometry.h FILE: ../../../flutter/impeller/entity/inline_pass_context.cc diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index a1a5704736260..2a69722f49610 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -23,6 +23,7 @@ #include "impeller/entity/contents/texture_contents.h" #include "impeller/entity/contents/vertices_contents.h" #include "impeller/entity/geometry/geometry.h" +#include "impeller/entity/geometry/superellipse_geometry.h" #include "impeller/geometry/color.h" #include "impeller/geometry/constants.h" #include "impeller/geometry/path_builder.h" diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index d91d56cdfe5fe..98e5831ac0ee6 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -196,6 +196,8 @@ impeller_component("entity") { "geometry/round_rect_geometry.h", "geometry/stroke_path_geometry.cc", "geometry/stroke_path_geometry.h", + "geometry/superellipse_geometry.cc", + "geometry/superellipse_geometry.h", "geometry/vertices_geometry.cc", "geometry/vertices_geometry.h", "inline_pass_context.cc", diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 7f476eb5281d6..92408fd7da6e2 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -39,6 +39,7 @@ #include "impeller/entity/geometry/geometry.h" #include "impeller/entity/geometry/point_field_geometry.h" #include "impeller/entity/geometry/stroke_path_geometry.h" +#include "impeller/entity/geometry/superellipse_geometry.h" #include "impeller/entity/render_target_cache.h" #include "impeller/geometry/color.h" #include "impeller/geometry/geometry_asserts.h" @@ -2587,6 +2588,37 @@ TEST_P(EntityTest, CanRenderEmptyPathsWithoutCrashing) { ASSERT_TRUE(OpenPlaygroundHere(std::move(entity))); } +TEST_P(EntityTest, DrawSuperEllipse) { + auto callback = [&](ContentContext& context, RenderPass& pass) -> bool { + // UI state. + static float alpha = 10; + static float beta = 10; + static float radius = 40; + static int degree = 4; + static Color color = Color::Red(); + + ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize); + ImGui::SliderFloat("Alpha", &alpha, 0, 100); + ImGui::SliderFloat("Beta", &beta, 0, 100); + ImGui::SliderInt("Degreee", °ree, 1, 20); + ImGui::SliderFloat("Radius", &radius, 0, 400); + ImGui::ColorEdit4("Color", reinterpret_cast(&color)); + ImGui::End(); + + auto contents = std::make_shared(); + contents->SetColor(color); + contents->SetGeometry(std::make_shared( + Point{400, 400}, radius, degree, alpha, beta)); + + Entity entity; + entity.SetContents(contents); + + return entity.Render(context, pass); + }; + + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + } // namespace testing } // namespace impeller diff --git a/impeller/entity/geometry/superellipse_geometry.cc b/impeller/entity/geometry/superellipse_geometry.cc new file mode 100644 index 0000000000000..578126b03c89d --- /dev/null +++ b/impeller/entity/geometry/superellipse_geometry.cc @@ -0,0 +1,111 @@ +// 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 + +#include "flutter/impeller/entity/geometry/superellipse_geometry.h" + +#include "impeller/geometry/constants.h" + +namespace impeller { + +SuperellipseGeometry::SuperellipseGeometry(const Point& center, + Scalar radius, + Scalar degree, + Scalar alpha, + Scalar beta) + : center_(center), + degree_(degree), + radius_(radius), + alpha_(alpha), + beta_(beta) {} + +GeometryResult SuperellipseGeometry::GetPositionBuffer( + const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + // https://math.stackexchange.com/questions/2573746/superellipse-parametric-equation + Scalar a = alpha_; + Scalar b = beta_; + Scalar n = degree_; + + // TODO(jonahwilliams): determine parameter values based on scaling factor. + Scalar step = kPi / 80; + + // Generate the points for the top left quadrant, and then mirror to the other + // quadrants. + std::vector points; + points.reserve(41); + for (int i = 0; i <= 40; i++) { + Scalar t = i * step; + Scalar x = a * pow(abs(cos(t)), 2 / n); + Scalar y = b * pow(abs(sin(t)), 2 / n); + points.emplace_back(x * radius_, y * radius_); + } + + static constexpr Point reflection[4] = {{1, 1}, {-1, 1}, {-1, -1}, {1, -1}}; + + // Reflect into the 4 quadrants and generate the tessellated mesh. The + // iteration order is reversed so that the trianges are continuous from + // quadrant to quadrant. + std::vector geometry; + geometry.reserve(1 + 4 * points.size()); + geometry.push_back(center_); + for (auto i = 0u; i < points.size(); i++) { + geometry.push_back(center_ + (reflection[0] * points[i])); + } + for (auto i = 0u; i < points.size(); i++) { + geometry.push_back(center_ + + (reflection[1] * points[points.size() - i - 1])); + } + for (auto i = 0u; i < points.size(); i++) { + geometry.push_back(center_ + (reflection[2] * points[i])); + } + for (auto i = 0u; i < points.size(); i++) { + geometry.push_back(center_ + + (reflection[3] * points[points.size() - i - 1])); + } + + std::vector indices; + indices.reserve(geometry.size() * 3); + for (auto i = 2u; i < geometry.size(); i++) { + indices.push_back(0); + indices.push_back(i - 1); + indices.push_back(i); + } + + auto& host_buffer = renderer.GetTransientsBuffer(); + return GeometryResult{ + .type = PrimitiveType::kTriangle, + .vertex_buffer = + { + .vertex_buffer = host_buffer.Emplace( + geometry.data(), geometry.size() * sizeof(Point), + alignof(Point)), + .index_buffer = host_buffer.Emplace( + indices.data(), indices.size() * sizeof(uint16_t), + alignof(uint16_t)), + .vertex_count = indices.size(), + .index_type = IndexType::k16bit, + }, + .transform = entity.GetShaderTransform(pass), + }; +} + +std::optional SuperellipseGeometry::GetCoverage( + const Matrix& transform) const { + return Rect::MakeOriginSize(center_ - Point(radius_, radius_), + Size(radius_ * 2, radius_ * 2)); +} + +bool SuperellipseGeometry::CoversArea(const Matrix& transform, + const Rect& rect) const { + return false; +} + +bool SuperellipseGeometry::IsAxisAlignedRect() const { + return false; +} + +} // namespace impeller diff --git a/impeller/entity/geometry/superellipse_geometry.h b/impeller/entity/geometry/superellipse_geometry.h new file mode 100644 index 0000000000000..19c049f2962ff --- /dev/null +++ b/impeller/entity/geometry/superellipse_geometry.h @@ -0,0 +1,62 @@ +// 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_ENTITY_GEOMETRY_SUPERELLIPSE_GEOMETRY_H_ +#define FLUTTER_IMPELLER_ENTITY_GEOMETRY_SUPERELLIPSE_GEOMETRY_H_ + +#include "impeller/entity/geometry/geometry.h" + +namespace impeller { + +/// Geometry class that can generate vertices for a superellipse. +/// +/// A Superellipse is an ellipse-like shape that is defined by the parameters N, +/// alpha, and beta: +/// +/// 1 = |x / b| ^n + |y / a| ^n +/// +/// The radius and center apply a uniform scaling and offset that is separate +/// from alpha or beta. When n = 4, the shape is referred to as a rectellipse. +/// +/// See also: https://en.wikipedia.org/wiki/Superellipse +class SuperellipseGeometry final : public Geometry { + public: + explicit SuperellipseGeometry(const Point& center, + Scalar radius, + Scalar degree, + Scalar alpha, + Scalar beta); + + ~SuperellipseGeometry() = default; + + // |Geometry| + bool CoversArea(const Matrix& transform, const Rect& rect) const override; + + // |Geometry| + bool IsAxisAlignedRect() const override; + + private: + // |Geometry| + GeometryResult GetPositionBuffer(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const override; + + // |Geometry| + std::optional GetCoverage(const Matrix& transform) const override; + + Point center_; + // 4 is a rectellipse + Scalar degree_; + Scalar radius_; + Scalar alpha_; + Scalar beta_; + + SuperellipseGeometry(const SuperellipseGeometry&) = delete; + + SuperellipseGeometry& operator=(const SuperellipseGeometry&) = delete; +}; + +} // namespace impeller + +#endif // FLUTTER_IMPELLER_ENTITY_GEOMETRY_SUPERELLIPSE_GEOMETRY_H_