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

📅  最后修改于: 2021-05-07 18:27:53             🧑  作者: Mango

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

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

离散化有几个应用程序,其中两个最重要的是:

  • 渲染曲线:曲线不能直接在屏幕上渲染。首先需要将它们近似为多边形(在“圆形”和椭圆形的情况下)或链线段(在“贝塞尔曲线”和样条曲线的情况下),离散化可以达到这个目的! *
  • 碰撞检测:虽然检查诸如Circle和Bezier曲线之类的曲线的交点很简单,但是诸如Ellipses之类的某些曲线却太复杂了,而精确地检查它们的交点是非常低效的!因此,它们首先被离散化为简单的形状,例如矩形,多边形等,可以有效地检测到它们的碰撞!

概念验证:因此,将椭圆离散化的主要思想是将椭圆分解为表示多边形的顶点,从而可以自动计算多边形的段数,也可以由客户用户指定多边形的段数!在此示例中,我们不会自动计算细分数!

我们将使用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++基础课程以及有关语言和STL的C++ STL课程。要完成从学习语言到DS Algo等的更多准备工作,请参阅“完整面试准备课程”