Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 68938ab

Browse files
author
Jonah Williams
authored
[Impeller] add support for superellipse. (#54562)
This is an entity testing only implementation of a superellipse. rectellipse is a special case where degree = 4. Part of flutter/flutter#139321
1 parent 65fd6ca commit 68938ab

File tree

6 files changed

+212
-0
lines changed

6 files changed

+212
-0
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42862,6 +42862,8 @@ ORIGIN: ../../../flutter/impeller/entity/geometry/round_rect_geometry.cc + ../..
4286242862
ORIGIN: ../../../flutter/impeller/entity/geometry/round_rect_geometry.h + ../../../flutter/LICENSE
4286342863
ORIGIN: ../../../flutter/impeller/entity/geometry/stroke_path_geometry.cc + ../../../flutter/LICENSE
4286442864
ORIGIN: ../../../flutter/impeller/entity/geometry/stroke_path_geometry.h + ../../../flutter/LICENSE
42865+
ORIGIN: ../../../flutter/impeller/entity/geometry/superellipse_geometry.cc + ../../../flutter/LICENSE
42866+
ORIGIN: ../../../flutter/impeller/entity/geometry/superellipse_geometry.h + ../../../flutter/LICENSE
4286542867
ORIGIN: ../../../flutter/impeller/entity/geometry/vertices_geometry.cc + ../../../flutter/LICENSE
4286642868
ORIGIN: ../../../flutter/impeller/entity/geometry/vertices_geometry.h + ../../../flutter/LICENSE
4286742869
ORIGIN: ../../../flutter/impeller/entity/inline_pass_context.cc + ../../../flutter/LICENSE
@@ -45739,6 +45741,8 @@ FILE: ../../../flutter/impeller/entity/geometry/round_rect_geometry.cc
4573945741
FILE: ../../../flutter/impeller/entity/geometry/round_rect_geometry.h
4574045742
FILE: ../../../flutter/impeller/entity/geometry/stroke_path_geometry.cc
4574145743
FILE: ../../../flutter/impeller/entity/geometry/stroke_path_geometry.h
45744+
FILE: ../../../flutter/impeller/entity/geometry/superellipse_geometry.cc
45745+
FILE: ../../../flutter/impeller/entity/geometry/superellipse_geometry.h
4574245746
FILE: ../../../flutter/impeller/entity/geometry/vertices_geometry.cc
4574345747
FILE: ../../../flutter/impeller/entity/geometry/vertices_geometry.h
4574445748
FILE: ../../../flutter/impeller/entity/inline_pass_context.cc

impeller/aiks/canvas.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "impeller/entity/contents/texture_contents.h"
2424
#include "impeller/entity/contents/vertices_contents.h"
2525
#include "impeller/entity/geometry/geometry.h"
26+
#include "impeller/entity/geometry/superellipse_geometry.h"
2627
#include "impeller/geometry/color.h"
2728
#include "impeller/geometry/constants.h"
2829
#include "impeller/geometry/path_builder.h"

impeller/entity/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ impeller_component("entity") {
196196
"geometry/round_rect_geometry.h",
197197
"geometry/stroke_path_geometry.cc",
198198
"geometry/stroke_path_geometry.h",
199+
"geometry/superellipse_geometry.cc",
200+
"geometry/superellipse_geometry.h",
199201
"geometry/vertices_geometry.cc",
200202
"geometry/vertices_geometry.h",
201203
"inline_pass_context.cc",

impeller/entity/entity_unittests.cc

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "impeller/entity/geometry/geometry.h"
4040
#include "impeller/entity/geometry/point_field_geometry.h"
4141
#include "impeller/entity/geometry/stroke_path_geometry.h"
42+
#include "impeller/entity/geometry/superellipse_geometry.h"
4243
#include "impeller/entity/render_target_cache.h"
4344
#include "impeller/geometry/color.h"
4445
#include "impeller/geometry/geometry_asserts.h"
@@ -2587,6 +2588,37 @@ TEST_P(EntityTest, CanRenderEmptyPathsWithoutCrashing) {
25872588
ASSERT_TRUE(OpenPlaygroundHere(std::move(entity)));
25882589
}
25892590

2591+
TEST_P(EntityTest, DrawSuperEllipse) {
2592+
auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
2593+
// UI state.
2594+
static float alpha = 10;
2595+
static float beta = 10;
2596+
static float radius = 40;
2597+
static int degree = 4;
2598+
static Color color = Color::Red();
2599+
2600+
ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
2601+
ImGui::SliderFloat("Alpha", &alpha, 0, 100);
2602+
ImGui::SliderFloat("Beta", &beta, 0, 100);
2603+
ImGui::SliderInt("Degreee", &degree, 1, 20);
2604+
ImGui::SliderFloat("Radius", &radius, 0, 400);
2605+
ImGui::ColorEdit4("Color", reinterpret_cast<float*>(&color));
2606+
ImGui::End();
2607+
2608+
auto contents = std::make_shared<SolidColorContents>();
2609+
contents->SetColor(color);
2610+
contents->SetGeometry(std::make_shared<SuperellipseGeometry>(
2611+
Point{400, 400}, radius, degree, alpha, beta));
2612+
2613+
Entity entity;
2614+
entity.SetContents(contents);
2615+
2616+
return entity.Render(context, pass);
2617+
};
2618+
2619+
ASSERT_TRUE(OpenPlaygroundHere(callback));
2620+
}
2621+
25902622
} // namespace testing
25912623
} // namespace impeller
25922624

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include <vector>
6+
7+
#include "flutter/impeller/entity/geometry/superellipse_geometry.h"
8+
9+
#include "impeller/geometry/constants.h"
10+
11+
namespace impeller {
12+
13+
SuperellipseGeometry::SuperellipseGeometry(const Point& center,
14+
Scalar radius,
15+
Scalar degree,
16+
Scalar alpha,
17+
Scalar beta)
18+
: center_(center),
19+
degree_(degree),
20+
radius_(radius),
21+
alpha_(alpha),
22+
beta_(beta) {}
23+
24+
GeometryResult SuperellipseGeometry::GetPositionBuffer(
25+
const ContentContext& renderer,
26+
const Entity& entity,
27+
RenderPass& pass) const {
28+
// https://math.stackexchange.com/questions/2573746/superellipse-parametric-equation
29+
Scalar a = alpha_;
30+
Scalar b = beta_;
31+
Scalar n = degree_;
32+
33+
// TODO(jonahwilliams): determine parameter values based on scaling factor.
34+
Scalar step = kPi / 80;
35+
36+
// Generate the points for the top left quadrant, and then mirror to the other
37+
// quadrants.
38+
std::vector<Point> points;
39+
points.reserve(41);
40+
for (int i = 0; i <= 40; i++) {
41+
Scalar t = i * step;
42+
Scalar x = a * pow(abs(cos(t)), 2 / n);
43+
Scalar y = b * pow(abs(sin(t)), 2 / n);
44+
points.emplace_back(x * radius_, y * radius_);
45+
}
46+
47+
static constexpr Point reflection[4] = {{1, 1}, {-1, 1}, {-1, -1}, {1, -1}};
48+
49+
// Reflect into the 4 quadrants and generate the tessellated mesh. The
50+
// iteration order is reversed so that the trianges are continuous from
51+
// quadrant to quadrant.
52+
std::vector<Point> geometry;
53+
geometry.reserve(1 + 4 * points.size());
54+
geometry.push_back(center_);
55+
for (auto i = 0u; i < points.size(); i++) {
56+
geometry.push_back(center_ + (reflection[0] * points[i]));
57+
}
58+
for (auto i = 0u; i < points.size(); i++) {
59+
geometry.push_back(center_ +
60+
(reflection[1] * points[points.size() - i - 1]));
61+
}
62+
for (auto i = 0u; i < points.size(); i++) {
63+
geometry.push_back(center_ + (reflection[2] * points[i]));
64+
}
65+
for (auto i = 0u; i < points.size(); i++) {
66+
geometry.push_back(center_ +
67+
(reflection[3] * points[points.size() - i - 1]));
68+
}
69+
70+
std::vector<uint16_t> indices;
71+
indices.reserve(geometry.size() * 3);
72+
for (auto i = 2u; i < geometry.size(); i++) {
73+
indices.push_back(0);
74+
indices.push_back(i - 1);
75+
indices.push_back(i);
76+
}
77+
78+
auto& host_buffer = renderer.GetTransientsBuffer();
79+
return GeometryResult{
80+
.type = PrimitiveType::kTriangle,
81+
.vertex_buffer =
82+
{
83+
.vertex_buffer = host_buffer.Emplace(
84+
geometry.data(), geometry.size() * sizeof(Point),
85+
alignof(Point)),
86+
.index_buffer = host_buffer.Emplace(
87+
indices.data(), indices.size() * sizeof(uint16_t),
88+
alignof(uint16_t)),
89+
.vertex_count = indices.size(),
90+
.index_type = IndexType::k16bit,
91+
},
92+
.transform = entity.GetShaderTransform(pass),
93+
};
94+
}
95+
96+
std::optional<Rect> SuperellipseGeometry::GetCoverage(
97+
const Matrix& transform) const {
98+
return Rect::MakeOriginSize(center_ - Point(radius_, radius_),
99+
Size(radius_ * 2, radius_ * 2));
100+
}
101+
102+
bool SuperellipseGeometry::CoversArea(const Matrix& transform,
103+
const Rect& rect) const {
104+
return false;
105+
}
106+
107+
bool SuperellipseGeometry::IsAxisAlignedRect() const {
108+
return false;
109+
}
110+
111+
} // namespace impeller
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef FLUTTER_IMPELLER_ENTITY_GEOMETRY_SUPERELLIPSE_GEOMETRY_H_
6+
#define FLUTTER_IMPELLER_ENTITY_GEOMETRY_SUPERELLIPSE_GEOMETRY_H_
7+
8+
#include "impeller/entity/geometry/geometry.h"
9+
10+
namespace impeller {
11+
12+
/// Geometry class that can generate vertices for a superellipse.
13+
///
14+
/// A Superellipse is an ellipse-like shape that is defined by the parameters N,
15+
/// alpha, and beta:
16+
///
17+
/// 1 = |x / b| ^n + |y / a| ^n
18+
///
19+
/// The radius and center apply a uniform scaling and offset that is separate
20+
/// from alpha or beta. When n = 4, the shape is referred to as a rectellipse.
21+
///
22+
/// See also: https://en.wikipedia.org/wiki/Superellipse
23+
class SuperellipseGeometry final : public Geometry {
24+
public:
25+
explicit SuperellipseGeometry(const Point& center,
26+
Scalar radius,
27+
Scalar degree,
28+
Scalar alpha,
29+
Scalar beta);
30+
31+
~SuperellipseGeometry() = default;
32+
33+
// |Geometry|
34+
bool CoversArea(const Matrix& transform, const Rect& rect) const override;
35+
36+
// |Geometry|
37+
bool IsAxisAlignedRect() const override;
38+
39+
private:
40+
// |Geometry|
41+
GeometryResult GetPositionBuffer(const ContentContext& renderer,
42+
const Entity& entity,
43+
RenderPass& pass) const override;
44+
45+
// |Geometry|
46+
std::optional<Rect> GetCoverage(const Matrix& transform) const override;
47+
48+
Point center_;
49+
// 4 is a rectellipse
50+
Scalar degree_;
51+
Scalar radius_;
52+
Scalar alpha_;
53+
Scalar beta_;
54+
55+
SuperellipseGeometry(const SuperellipseGeometry&) = delete;
56+
57+
SuperellipseGeometry& operator=(const SuperellipseGeometry&) = delete;
58+
};
59+
60+
} // namespace impeller
61+
62+
#endif // FLUTTER_IMPELLER_ENTITY_GEOMETRY_SUPERELLIPSE_GEOMETRY_H_

0 commit comments

Comments
 (0)