📜  线剪裁 |第 2 组(Cyrus Beck 算法)

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

背景
Cyrus Beck 是一种为凸多边形设计的线裁剪算法。与 Cohen Sutherland 或 Nicholl Le Nicholl不同,它允许对非矩形窗口进行线剪裁。它还删除了 Cohen Sutherland 中所需的重复剪辑。

Input: 
 1. Convex area of interest 
    which is defined by a set of coordinates
    given in a clockwise fashion.
 2. vertices which are an array of coordinates: 
    consisting of pairs (x, y)
 3. n which is the number of vertices
 4. A line to be clipped 
    given by a set of coordinates.
 5. line which is an array of coordinates: 
    consisting of two pairs, (x0, y0) and (x1, y1)
Output:
 1. Coordinates of line clipping which is the Accepted clipping
 2. Coordinates (-1, -1) which is the Rejected clipping

算法:

  • 计算每条边的法线。
  • 计算剪切线的向量。
  • 计算每条边的一个顶点与剪裁线的一个选定端点的差与边的法线之间的点积(对于所有边)。
  • 计算剪裁线向量与边缘法线(对于所有边缘)之间的点积。
  • 前者的点积除以后者的点积再乘以-1。这是’不是’。
  • ‘t’ 的值通过观察它们的分母(后一个点积)被分类为进入或退出(从所有边缘)。
  • 从每组中选择一个 ‘t’ 值,并放入一条线的参数形式来计算坐标。
  • 如果输入的 ‘t’ 值大于退出的 ‘t’ 值,则拒绝剪裁线。

案例

  1. 情况 1:线部分位于剪辑窗口内:
    0 < tE < tL < 1
    
    where tE is 't' value for entering intersection point
          tL is 't' value for exiting intersection point
    
  2. 情况 2:该线在窗口内或两侧有一个点或交点在该线的端点上
    0 ≤ tE ≤ tL ≤ 1
  3. 情况3:线路完全在窗外:
    tL < tE

伪代码:

首先计算要剪裁的线的参数形式,然后按照算法进行。

  • 从直线的两个点 (P 0 P 1 ) 中选择一个称为 P 1 的点。
  • 现在,对于多边形的每条边,计算远离多边形中心的法线,即 N 1 、N 2等。
  • 现在为每条边选择 P Ei (i -> i th edge)(选择相应边的任何顶点,例如:对于多边形 ABCD,对于边 AB,P Ei可以是点 A 或点 B)并计算
    P0 - PEi 
  • 然后计算
    P1 - P0
  • 然后为每条边计算以下点积:
    Ni . (P0 - PEi)
    Ni . (P1 - P0) 
    
    where i -> ith edge of the convex polygon
  • 然后通过以下方式计算每条边对应的 ‘t’ 值:
    Ni . (P0 - PEi)
    t = ------------------
        -(Ni . (P1 - P0))
  • 然后将 N i 所对应的“t”值加入俱乐部。 (P 1 – P 0 )结果为负数,取其中的最小值和 1。
  • 类似地,将 N i 所对应的所有“t”值都包含在内。 (P 1 – P 0 ) 结果为正,取所有棒状 ‘t’ 值和 0 中的最大值。
  • 现在,从该算法中获得的两个 ‘t’ 值被插入到“待剪裁”线的参数形式中,得到的两个点就是剪裁点。

      实现这里是SFML C++图形库中上述步骤的实现。您也可以按任意键取消剪裁线条,按任意键剪裁线条。

      // C++ Program to implement Cyrus Beck
        
      #include 
      #include 
      #include 
      #include 
        
      using namespace std;
      using namespace sf;
        
      // Function to draw a line in SFML
      void drawline(RenderWindow* window, pair p0, pair p1)
      {
          Vertex line[] = {
              Vertex(Vector2f(p0.first, p0.second)),
              Vertex(Vector2f(p1.first, p1.second))
          };
          window->draw(line, 2, Lines);
      }
        
      // Function to draw a polygon, given vertices
      void drawPolygon(RenderWindow* window, pair vertices[], int n)
      {
          for (int i = 0; i < n - 1; i++)
              drawline(window, vertices[i], vertices[i + 1]);
          drawline(window, vertices[0], vertices[n - 1]);
      }
        
      // Function to take dot product
      int dot(pair p0, pair p1)
      {
          return p0.first * p1.first + p0.second * p1.second;
      }
        
      // Function to calculate the max from a vector of floats
      float max(vector t)
      {
          float maximum = INT_MIN;
          for (int i = 0; i < t.size(); i++)
              if (t[i] > maximum)
                  maximum = t[i];
          return maximum;
      }
        
      // Function to calculate the min from a vector of floats
      float min(vector t)
      {
          float minimum = INT_MAX;
          for (int i = 0; i < t.size(); i++)
              if (t[i] < minimum)
                  minimum = t[i];
          return minimum;
      }
        
      // Cyrus Beck function, returns a pair of values
      // that are then displayed as a line
      pair* CyrusBeck(pair vertices[],
                                pair line[], int n)
      {
        
          // Temporary holder value that will be returned
          pair* newPair = new pair[2];
        
          // Normals initialized dynamically(can do it statically also, doesn't matter)
          pair* normal = new pair[n];
        
          // Calculating the normals
          for (int i = 0; i < n; i++) {
              normal[i].second = vertices[(i + 1) % n].first - vertices[i].first;
              normal[i].first = vertices[i].second - vertices[(i + 1) % n].second;
          }
        
          // Calculating P1 - P0
          pair P1_P0
              = make_pair(line[1].first - line[0].first,
                          line[1].second - line[0].second);
        
          // Initializing all values of P0 - PEi
          pair* P0_PEi = new pair[n];
        
          // Calculating the values of P0 - PEi for all edges
          for (int i = 0; i < n; i++) {
        
              // Calculating PEi - P0, so that the
              // denominator won't have to multiply by -1
              P0_PEi[i].first
                  = vertices[i].first - line[0].first;
        
              // while calculating 't'
              P0_PEi[i].second = vertices[i].second - line[0].second;
          }
        
          int *numerator = new int[n], *denominator = new int[n];
        
          // Calculating the numerator and denominators
          // using the dot function
          for (int i = 0; i < n; i++) {
              numerator[i] = dot(normal[i], P0_PEi[i]);
              denominator[i] = dot(normal[i], P1_P0);
          }
        
          // Initializing the 't' values dynamically
          float* t = new float[n];
        
          // Making two vectors called 't entering'
          // and 't leaving' to group the 't's
          // according to their denominators
          vector tE, tL;
        
          // Calculating 't' and grouping them accordingly
          for (int i = 0; i < n; i++) {
        
              t[i] = (float)(numerator[i]) / (float)(denominator[i]);
        
              if (denominator[i] > 0)
                  tE.push_back(t[i]);
              else
                  tL.push_back(t[i]);
          }
        
          // Initializing the final two values of 't'
          float temp[2];
        
          // Taking the max of all 'tE' and 0, so pushing 0
          tE.push_back(0.f);
          temp[0] = max(tE);
        
          // Taking the min of all 'tL' and 1, so pushing 1
          tL.push_back(1.f);
          temp[1] = min(tL);
        
          // Entering 't' value cannot be
          // greater than exiting 't' value,
          // hence, this is the case when the line
          // is completely outside
          if (temp[0] > temp[1]) {
              newPair[0] = make_pair(-1, -1);
              newPair[1] = make_pair(-1, -1);
              return newPair;
          }
        
          // Calculating the coordinates in terms of x and y
          newPair[0].firs
              t
              = (float)line[0].first
                + (float)P1_P0.first * (float)temp[0];
          newPair[0].second
              = (float)line[0].second
                + (float)P1_P0.second * (float)temp[0];
          newPair[1].first
              = (float)line[0].first
                + (float)P1_P0.first * (float)temp[1];
          newPair[1].second
              = (float)line[0].second
                + (float)P1_P0.second * (float)temp[1];
          cout << '(' << newPair[0].first << ", "
               << newPair[0].second << ") ("
               << newPair[1].first << ", "
               << newPair[1].second << ")";
        
          return newPair;
      }
        
      // Driver code
      int main()
      {
        
          // Setting up a window and loop
          // and the vertices of the polygon and line
          RenderWindow window(VideoMode(500, 500), "Cyrus Beck");
          pair vertices[]
              = { make_pair(200, 50),
                  make_pair(250, 100),
                  make_pair(200, 150),
                  make_pair(100, 150),
                  make_pair(50, 100),
                  make_pair(100, 50) };
        
          // Make sure that the vertices
          // are put in a clockwise order
          int n = sizeof(vertices) / sizeof(vertices[0]);
          pair line[] = { make_pair(10, 10), make_pair(450, 200) };
          pair* temp1 = CyrusBeck(vertices, line, n);
          pair temp2[2];
          temp2[0] = line[0];
          temp2[1] = line[1];
        
          // To allow clipping and unclipping
          // of the line by just pressing a key
          bool trigger = false;
          while (window.isOpen()) {
              window.clear();
              Event event;
              if (window.pollEvent(event)) {
                  if (event.type == Event::Closed)
                      window.close();
                  if (event.type == Event::KeyPressed)
                      trigger = !trigger;
              }
              drawPolygon(&window, vertices, n);
        
              // Using the trigger value to clip
              // and unclip a line
              if (trigger) {
                  line[0] = temp1[0];
                  line[1] = temp1[1];
              }
              else {
                  line[0] = temp2[0];
                  line[1] = temp2[1];
              }
              drawline(&window, line[0], line[1]);
              window.display();
          }
          return 0;
      }
      

      输出:

      (102, 50) (240, 109)
    • 剪辑前:

    • 剪辑后:

    如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程学生竞争性编程现场课程。