📜  如何使用 C++ 图形将椭圆或圆离散为多边形?

📅  最后修改于: 2021-10-23 08:55:29             🧑  作者: Mango

在本文中,我们将看到如何将椭圆(或圆)离散为多边形。由于圆只是椭圆的一个特例,我们不会单独讨论如何将圆离散化为多边形!

为什么将椭圆离散化为多边形?

离散化有多种应用,其中最重要的两个是:

  • 渲染曲线:不能直接在屏幕上渲染曲线。它们首先需要近似为多边形(在圆形和椭圆的情况下)或链线段(在贝塞尔曲线和样条的情况下)并且离散化服务于这个目的! *
  • 碰撞检测:虽然检查圆和贝塞尔曲线等曲线的交点很简单,但像椭圆这样的曲线太复杂了,精确地检查它们的交点效率非常低!因此,它们首先被离散化为简单的形状,如矩形、多边形等,可以有效地检测碰撞!

概念验证:因此,离散化椭圆的主要思想是将椭圆分解为表示多边形的顶点,由此多边形的段数将自动计算或由客户端用户给出!对于这个例子,我们不会自动计算段数!

我们将使用 C Borland Graphics API,但同样的原则可以应用于任何图形库! Linux 用户可能希望使用 SDL libgraph 作为 Borland 图形库的替代品!此外,我们将使用 C++ 而不是 C 来使用 STL 提供的内置数据结构pair (以及稍后会派上用场的函数重载)!

渲染多边形:

事实证明,Borland Graphics API 实际上并没有像现代图形库那样的多格式多边形渲染函数!有 drawPoly 和 fillPoly 但多边形的表示对某些人来说可能没有意义,也可能导致我们遇到一些指针问题。所以我们将实现我们自己的多边形渲染函数!我们将多边形表示为代表多边形顶点的整数对向量!这样做的主要好处是向量总是知道它的大小,这与数组不同,后者在底层只是一个未知边界的指针。

无论如何,这是我们在 Borland 图形库中渲染多边形的代码:-

#include 
#include 
using namespace std;
  
typedef pair vertex;
  
void polygon(vector& vertices)
{
    for (int i = 0, n = vertices.size(); i < n; i++) {
  
        vertex current = vertices[i], next;
        next = vertices[(i == n - 1) ? 0 : i + 1];
        int x1 = current.first, y1 = current.second;
        int x2 = next.first, y2 = next.second;
        line(x1, y1, x2, y2);
    }
}
  
// Driver code
int main()
{
    int gd = DETECT, gm;
  
    // initialize graphics library
    initgraph(&gd, &gm, "");
  
    vector vertices;
    vertices.push_back(vertex(340, 150));
    vertices.push_back(vertex(220, 250));
    vertices.push_back(vertex(340, 350));
  
    polygon(vertices);
    delay(5000);
}

输出:

将椭圆离散为多边形:

现在我们可以渲染多边形了,我们准备将椭圆离散化为多边形!

因此,离散化椭圆的关键是让一个移动点以相等的间隔穿过椭圆,并在每个这样的点(移动点经过的每个点)上创建一个顶点!为此,必须知道椭圆的参数形式,即:-

 x=acos(\theta )
 y=bsin(\theta )

基于上述公式,这是将我们的椭圆离散化为多边形的代码片段:-

#define TWO_PI 44 / 7.0f
  
vector discretizeEllipse(
    int x, int y,
    int a, int b,
    int seg)
{
  
    float angle_shift = TWO_PI / seg, phi = 0;
    vector vertices;
    for (int i = 0; i < seg; ++i) {
        phi += angle_shift;
        vertices
            .push_back(
                vertex(
                    x + a * cos(phi),
                    y + b * sin(phi)));
    }
    return vertices;
}

剩下要做的最后一件事是重载函数,以便不需要最后一个参数!我们可以将segments设置为某个默认值,但我们希望根据椭圆的尺寸来计算它!所以这是第二个重载:-

vector discretizeEllipse(
    int x, int y,
    int a, int b)
{
    int segments
        = max((int)floor(
                  sqrt(((a + b) / 2) * 20)),
              8);
    return discretizeEllipse(
        x, y,
        a, b,
        segments);
}

为了结束本文,这里是完整的源代码:

#include 
#include 
  
#define TWO_PI 44 / 7.0f
typedef pair vertex;
  
void polygon(vector vertices)
{
    for (int i = 0, n = vertices.size(); i < n; i++) {
        vertex current = vertices[i], next;
        next = vertices[(i == n - 1) ? 0 : i + 1];
        int x1 = current.first, y1 = current.second;
        int x2 = next.first, y2 = next.second;
        line(x1, y1, x2, y2);
    }
}
  
vector discretizeEllipse(
    int x, int y, int a,
    int b, int seg)
{
    float angle_shift = TWO_PI / seg, phi = 0;
    vector vertices;
    for (int i = 0; i < seg; ++i) {
        phi += angle_shift;
        vertices.push_back(
            vertex(
                x + a * cos(phi),
                y + b * sin(phi)));
    }
  
    return vertices;
}
  
vector discretizeEllipse(
    int x, int y, int a, int b)
{
    int segments
        = max((int)floor(
                  sqrt(((a + b) / 2) * 20)),
              8);
  
    return discretizeEllipse(
        x, y, a, b, segments);
}
  
int main()
{
    int gd = DETECT, gm;
  
    // initialize graphics library
    initgraph(&gd, &gm, "");
  
    polygon(discretizeEllipse(320, 240, 200, 100));
    polygon(discretizeEllipse(320, 240, 200, 100, 8));
  
    delay(5000);
}

输出:

想要从精选的视频和练习题中学习,请查看C++ 基础课程,从基础到高级 C++ 和C++ STL 课程,了解语言和 STL。要完成从学习语言到 DS Algo 等的准备工作,请参阅完整的面试准备课程