如何为自定义旋转的2D Box实现全填充效果?
实现旋转2D矩形的全填充功能(128x64 OLED)
需求背景
我维护一个Box_2D类,用于在128x64 OLED屏上绘制带旋转的矩形,目前支持无填充和十字填充两种模式,需要新增全矩形填充功能。硬件当前用Arduino Uno,后续将切换到ESP-WROOM-32。Vector2是自定义的二维坐标类,现有代码如下:
//128x64 OLED using enum FillType { FILLTYPE_NONE, FILLTYPE_CROSS, FILLTYPE_FAST_FILL, FILLTYPE_FILL }; class Vector2 { public: Vector2(int nx, int ny) {x = nx; y = ny;} Vector2 Addx(Vector2 a, Vector2 b) { return Vector2(a.x + b.x, a.y + b.y); } int x; int y; }; class Empty_2D { public: /* Deleted functions: position() >> returns Vector2 rotation() >> returns float SetPosition(Vector2) SetRotation(float) SetPivot(Vector2) */ private: int posX; int posY; int pivotX; int pivotY; float local_angle; }; class Box_2D { public: /* Deleted functions: SetPivot(Vector2) [set center] SetPosition(Vector2) SetRotation(float) */ void Draw(FillType fill) { oled.line(point1.position().x, point1.position().y, point2.position().x, point2.position().y); oled.line(point2.position().x, point2.position().y, point3.position().x, point3.position().y); oled.line(point3.position().x, point3.position().y, point4.position().x, point4.position().y); oled.line(point4.position().x, point4.position().y, point1.position().x, point1.position().y); if(fill == FILLTYPE_CROSS) { oled.line(point1.position().x, point1.position().y, point3.position().x, point3.position().y); oled.line(point2.position().x, point2.position().y, point4.position().x, point4.position().y); } if(fill == FILLTYPE_FILL) { //idk } } private: int pivot_x; int pivot_y; Empty_2D point1; Empty_2D point2; Empty_2D point3; Empty_2D point4; };
之前尝试用两个矩形拼接填充,但未找到可行方案。
解决方案:扫描线填充算法
针对旋转矩形的填充,小分辨率OLED最适合的方案是扫描线填充——逐行(Y轴)扫描,找到当前行与矩形四条边的交点,然后在两个交点之间画水平线。
步骤1:补充Vector2辅助函数
先给Vector2类添加计算交点的静态方法:
class Vector2 { public: Vector2(int nx, int ny) {x = nx; y = ny;} Vector2 Addx(Vector2 a, Vector2 b) { return Vector2(a.x + b.x, a.y + b.y); } // 根据Y值计算两点连线上的X坐标(求交点用) static int GetXAtY(Vector2 p1, Vector2 p2, int y) { if (p1.y == p2.y) return p1.x; // 水平线直接返回X // 线性插值计算X return p1.x + ((y - p1.y) * (p2.x - p1.x)) / (p2.y - p1.y); } int x; int y; };
步骤2:实现填充逻辑
修改Box_2D::Draw函数中的FILLTYPE_FILL分支:
if(fill == FILLTYPE_FILL) { // 1. 获取四个顶点的屏幕坐标 Vector2 p1 = point1.position(); Vector2 p2 = point2.position(); Vector2 p3 = point3.position(); Vector2 p4 = point4.position(); // 2. 确定扫描线的Y范围(最小到最大Y值) int minY = p1.y, maxY = p1.y; minY = min(minY, min(p2.y, min(p3.y, p4.y))); maxY = max(maxY, max(p2.y, max(p3.y, p4.y))); // 3. 逐行扫描填充 for(int y = minY; y <= maxY; y++){ int intersections[4]; int count = 0; // 检查每条边是否与当前Y线相交 auto checkEdge = [&](Vector2 a, Vector2 b) { if((a.y <= y && b.y >= y) || (a.y >= y && b.y <= y)){ intersections[count++] = Vector2::GetXAtY(a, b, y); } }; checkEdge(p1, p2); checkEdge(p2, p3); checkEdge(p3, p4); checkEdge(p4, p1); // 4. 排序交点,画水平线 if(count >= 2){ int minX = intersections[0], maxX = intersections[0]; for(int i=1; i<count; i++){ minX = min(minX, intersections[i]); maxX = max(maxX, intersections[i]); } // 限制在OLED屏幕范围内(128x64) minX = constrain(minX, 0, 127); maxX = constrain(maxX, 0, 127); oled.line(minX, y, maxX, y); } } }
注意事项
- 该算法计算量小,Arduino Uno完全胜任,ESP32运行会更流畅
constrain函数用于避免超出OLED的128x64显示边界- 若矩形完全在屏幕外,扫描循环会自动跳过,无无效计算
内容的提问来源于stack exchange,提问作者Uncnown Cube
相关产品推荐
相关产品推荐

