You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为自定义旋转的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 19:41:09