Skip to content

Commit 3081a89

Browse files
committed
Comprehensive performance optimization
- Eliminate duplicate rendering on touch events - Implement circumcircle caching - Reuse triangle buffer to reduce allocations - Optimize sqrt() calculations in physics - Enhance line rendering quality with bidirectional offset - Adjust frame rate to 60fps (10ms→16ms delay) - Enable aggressive compiler optimizations (-O3, -ffast-math, -funroll-loops)
1 parent a27a4dc commit 3081a89

8 files changed

Lines changed: 89 additions & 65 deletions

File tree

include/DelaunayTriangulation.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ class DelaunayTriangulation {
1313
private:
1414
std::vector<Point>& points;
1515
M5Canvas& canvas;
16+
17+
// Reusable buffer for triangles to avoid reallocations
18+
std::vector<Triangle> triangleCache;
1619

1720
// Find all Delaunay triangles from the current set of points
18-
std::vector<Triangle> findDelaunayTriangles();
21+
void findDelaunayTriangles();
1922

2023
public:
2124
// Constructor

include/Point.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,15 @@ class Point {
5050
float dy = y - other.y;
5151
float distSq = dx * dx + dy * dy;
5252

53-
if (distSq < radius * radius && distSq > 0) {
53+
float radiusSq = radius * radius;
54+
if (distSq < radiusSq && distSq > 0.01f) { // Avoid division by very small numbers
55+
// Use inverse sqrt approximation for better performance
5456
float dist = sqrt(distSq);
55-
float force = strength * (1.0 - dist / radius);
57+
float force = strength * (1.0f - dist / radius);
58+
float invDist = 1.0f / dist;
5659

57-
vx += dx / dist * force;
58-
vy += dy / dist * force;
60+
vx += dx * invDist * force;
61+
vy += dy * invDist * force;
5962
}
6063
}
6164

include/TouchHandler.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ class TouchHandler {
1919
TouchHandler(PointManager& pointManager);
2020

2121
// Update touch state and handle touch events
22-
// Returns true if a touch event was processed
23-
bool update();
22+
void update();
2423

2524
// Get the current touch state
2625
bool isTouching() const { return wasTouch; }

include/Triangle.h

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,15 @@
44

55
// Triangle class representing a triangle formed by three points
66
class Triangle {
7-
public:
8-
Point* p1;
9-
Point* p2;
10-
Point* p3;
11-
12-
// Constructor
13-
Triangle(Point* _p1, Point* _p2, Point* _p3)
14-
: p1(_p1), p2(_p2), p3(_p3) {}
15-
16-
// Check if a point is inside the circumcircle of this triangle
17-
bool isPointInCircumcircle(const Point& p) const {
7+
private:
8+
// Cached circumcircle data
9+
mutable bool circumcircleCalculated;
10+
mutable float centerX, centerY, radiusSquared;
11+
12+
// Calculate and cache circumcircle parameters
13+
void calculateCircumcircle() const {
14+
if (circumcircleCalculated) return;
15+
1816
// Three points of the triangle
1917
float x1 = p1->x;
2018
float y1 = p1->y;
@@ -27,7 +25,11 @@ class Triangle {
2725
float a = x1 * (y2 - y3) - y1 * (x2 - x3) + x2 * y3 - x3 * y2;
2826

2927
// Handle degenerate case (colinear points)
30-
if (fabs(a) < 1e-6) return false;
28+
if (fabs(a) < 1e-6) {
29+
radiusSquared = -1.0f; // Invalid circumcircle
30+
circumcircleCalculated = true;
31+
return;
32+
}
3133

3234
float b = (x1 * x1 + y1 * y1) * (y3 - y2) +
3335
(x2 * x2 + y2 * y2) * (y1 - y3) +
@@ -42,9 +44,30 @@ class Triangle {
4244
(x3 * x3 + y3 * y3) * (x2 * y1 - x1 * y2);
4345

4446
// Center and squared radius of circumcircle
45-
float centerX = -b / (2 * a);
46-
float centerY = -c / (2 * a);
47-
float radiusSquared = (b * b + c * c - 4 * a * d) / (4 * a * a);
47+
float invA2 = 1.0f / (2.0f * a);
48+
centerX = -b * invA2;
49+
centerY = -c * invA2;
50+
radiusSquared = (b * b + c * c - 4 * a * d) / (4 * a * a);
51+
52+
circumcircleCalculated = true;
53+
}
54+
55+
public:
56+
Point* p1;
57+
Point* p2;
58+
Point* p3;
59+
60+
// Constructor
61+
Triangle(Point* _p1, Point* _p2, Point* _p3)
62+
: p1(_p1), p2(_p2), p3(_p3), circumcircleCalculated(false) {}
63+
64+
// Check if a point is inside the circumcircle of this triangle
65+
bool isPointInCircumcircle(const Point& p) const {
66+
// Calculate circumcircle if not already done
67+
calculateCircumcircle();
68+
69+
// Check if circumcircle is valid
70+
if (radiusSquared < 0) return false;
4871

4972
// Squared distance between point and center
5073
float distSq = (p.x - centerX) * (p.x - centerX) +

platformio.ini

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ lib_deps =
77
m5stack/M5Unified
88

99
build_flags =
10-
-I include
10+
-O3
11+
-ffast-math
12+
-funroll-loops

src/DelaunayTriangulation.cpp

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,31 @@
33
// Constructor
44
DelaunayTriangulation::DelaunayTriangulation(std::vector<Point>& points, M5Canvas& canvas)
55
: points(points), canvas(canvas) {
6+
// Pre-allocate triangle cache to avoid frequent reallocations
7+
triangleCache.reserve(60); // Estimate: ~2x MAX_POINTS
68
}
79

810
// Find all Delaunay triangles from the current set of points
9-
std::vector<Triangle> DelaunayTriangulation::findDelaunayTriangles() {
10-
std::vector<Triangle> delaunayTriangles;
11+
void DelaunayTriangulation::findDelaunayTriangles() {
12+
// Clear previous triangles but keep capacity
13+
triangleCache.clear();
1114

1215
// Need at least 3 points to form a triangle
1316
if (points.size() < 3) {
14-
return delaunayTriangles;
17+
return;
1518
}
1619

20+
size_t n = points.size();
21+
1722
// Check all possible triangles
18-
for (size_t i = 0; i < points.size() - 2; i++) {
19-
for (size_t j = i + 1; j < points.size() - 1; j++) {
20-
for (size_t k = j + 1; k < points.size(); k++) {
23+
for (size_t i = 0; i < n - 2; i++) {
24+
for (size_t j = i + 1; j < n - 1; j++) {
25+
for (size_t k = j + 1; k < n; k++) {
2126
Triangle tri(&points[i], &points[j], &points[k]);
2227

2328
// Check if no other points are inside this triangle's circumcircle
2429
bool isDelaunay = true;
25-
for (size_t l = 0; l < points.size(); l++) {
30+
for (size_t l = 0; l < n; l++) {
2631
if (l != i && l != j && l != k) {
2732
if (tri.isPointInCircumcircle(points[l])) {
2833
isDelaunay = false;
@@ -31,15 +36,13 @@ std::vector<Triangle> DelaunayTriangulation::findDelaunayTriangles() {
3136
}
3237
}
3338

34-
// Add to list if it satisfies Delaunay condition
39+
// Add to cache if it satisfies Delaunay condition
3540
if (isDelaunay) {
36-
delaunayTriangles.push_back(tri);
41+
triangleCache.push_back(tri);
3742
}
3843
}
3944
}
4045
}
41-
42-
return delaunayTriangles;
4346
}
4447

4548
// Calculate and draw the Delaunay triangulation
@@ -49,10 +52,11 @@ void DelaunayTriangulation::calculateAndDraw() {
4952

5053
// If we have enough points, find and draw triangles
5154
if (points.size() >= 3) {
52-
std::vector<Triangle> triangles = findDelaunayTriangles();
55+
// Find triangles (fills triangleCache)
56+
findDelaunayTriangles();
5357

54-
// Draw each triangle
55-
for (const auto& triangle : triangles) {
58+
// Draw each triangle from cache
59+
for (const auto& triangle : triangleCache) {
5660
uint16_t color = ColorUtils::generatePastelColor(triangle.getColorSeed());
5761
drawTriangle(triangle, color);
5862
}
@@ -68,15 +72,19 @@ void DelaunayTriangulation::calculateAndDraw() {
6872
// Draw a single triangle with the specified color
6973
void DelaunayTriangulation::drawTriangle(const Triangle& triangle, uint16_t color) {
7074
// Draw each line with the specified thickness
71-
for (int i = 0; i < DisplayConstants::LINE_THICKNESS; i++) {
72-
canvas.drawLine(triangle.p1->x + i, triangle.p1->y,
73-
triangle.p2->x + i, triangle.p2->y, color);
74-
75-
canvas.drawLine(triangle.p2->x + i, triangle.p2->y,
76-
triangle.p3->x + i, triangle.p3->y, color);
77-
78-
canvas.drawLine(triangle.p3->x + i, triangle.p3->y,
79-
triangle.p1->x + i, triangle.p1->y, color);
75+
// Draw offset in both x and y directions for more uniform thickness
76+
int thickness = DisplayConstants::LINE_THICKNESS;
77+
for (int dx = 0; dx < thickness; dx++) {
78+
for (int dy = 0; dy < thickness; dy++) {
79+
canvas.drawLine(triangle.p1->x + dx, triangle.p1->y + dy,
80+
triangle.p2->x + dx, triangle.p2->y + dy, color);
81+
82+
canvas.drawLine(triangle.p2->x + dx, triangle.p2->y + dy,
83+
triangle.p3->x + dx, triangle.p3->y + dy, color);
84+
85+
canvas.drawLine(triangle.p3->x + dx, triangle.p3->y + dy,
86+
triangle.p1->x + dx, triangle.p1->y + dy, color);
87+
}
8088
}
8189
}
8290

src/TouchHandler.cpp

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ TouchHandler::TouchHandler(PointManager& pointManager)
66
}
77

88
// Update touch state and handle touch events
9-
bool TouchHandler::update() {
9+
void TouchHandler::update() {
1010
// Get current touch state
1111
bool isTouch = (M5.Touch.getCount() > 0);
1212

@@ -25,19 +25,10 @@ bool TouchHandler::update() {
2525

2626
// Apply repulsion to other points
2727
pointManager.applyRepulsion(newPoint);
28-
29-
// Update touch state
30-
wasTouch = isTouch;
31-
32-
// Return true to indicate a touch event was processed
33-
return true;
3428
}
3529

3630
// Update touch state
3731
wasTouch = isTouch;
38-
39-
// Return false to indicate no touch event was processed
40-
return false;
4132
}
4233

4334
// Play feedback sound for touch

src/main.cpp

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,14 @@ void loop() {
5353
M5.update();
5454

5555
// Handle touch input
56-
bool touchProcessed = touchHandler.update();
57-
58-
// If touch was processed, redraw the triangulation
59-
if (touchProcessed) {
60-
triangulation.calculateAndDraw();
61-
}
56+
touchHandler.update();
6257

6358
// Update point positions
6459
pointManager.updatePoints(M5.Display.width(), M5.Display.height());
6560

66-
// Recalculate and draw Delaunay triangulation
61+
// Recalculate and draw Delaunay triangulation (only once per frame)
6762
triangulation.calculateAndDraw();
6863

69-
// Small delay to control frame rate
70-
delay(10);
64+
// Small delay to control frame rate (16ms ≈ 60fps)
65+
delay(16);
7166
}

0 commit comments

Comments
 (0)