• 1.图形学-矩形中的线段的裁剪Cohen-Sutherland算法(附带源码)


    1.矩形中点的裁剪

    判断一个点是否在一个矩形中,必须满足:

    • 矩形左边的X <= 点的X <= 矩形右边的X
    • 矩形顶部的Y <= 点的Y <= 矩形底部的Y
       

    2.矩形窗口直线的裁剪

    分为4种,本章只学习直接求交算法、Cohen-Sutherland算法(编码裁剪算法)
     

    3.直接求交算法(复杂图形裁剪的基础)

    对于每条线段(P1,P2)分为三种情况处理:
    (1)    若P1,P2完全在窗口内,则显示该线段
    线段的p1和p2都要满足以下要求

    • Xleft <= 点的X <=  Xright
    • Y bottom<= 点的Y <=  Ytop

    (2)若P1,P2在窗口外且无交点,则丢弃该线段。 线段的p1(x1,y1)和p2(x2,y2)满足以下要求之一

    1. (x1 < Xleft  &&  x2< Xleft) ||
    2. (x1 > Xright  &&  x2> Xright) ||
    3. (y1 < Ybottom  &&  y2< Ybottom) ||
    4. (y1 > Ytop  &&  y2 > Ytop)

    (3)若线段不满足(1)或(2)的条件,则在交点处把线段分为两段。其中一段完全在窗口外,可弃之。然后对另一段重复上述处理 

    如下图GH为例:  


    由于G和H在窗口外且与窗口有相交,所以不满足(1)或(2)的条件,然后将交点a处把线段分为两段: Ga、aH、由于Ga在窗口外,则重复执行aH线段处理、直到得出最后为ab线段

     
    4.Cohen-Sutherland算法(编码裁剪算法)

    在直接求交算法的基础上,对窗口进行延伸得到九个区域,对每个区域进行编码(上下右左).如下图所示:


    其中编码上每位标志为:

     
    裁剪线段规律如下所示(code1为线段端点1, code2为线段端点2):

    • (1)  code1 | code2 = 0 则表示在窗口内,则显示该线段
    • (2)  code1 & code2 != 0 则表示完全在窗口外且不相交, 则丢弃该线段
    • (3)  若线段不满足(1)或(2)的条件,则在交点处把线段分为两段。其中一段完全在窗口外,可弃之。然后对另一段重复上述处理

    比如有一条线段与界面相交如下所示:

     
    得出:

    1. P1(code1)  =  0001
    2. P2(code2)  =  1000

    由于code1| code2 != 0 且 code1 & cpde2 = 0,得出线段与界面相交, 则在交点处把线段分为两段。其中一段完全在窗口外,可弃之。然后对另一段重复上述处理

    如何计算线段p1(x1,y1)和p2(x2,y2)在窗口边界的交点?
    很简单,根据斜率比乘上窗口边界的值在加上偏移值即可得出.对于x和y的公式如下所示:

    1. y = y1 + 斜率 * (x - x1)
    2. x = x1 + (1 / 斜率) * (y - y1) 

    窗口的上下左右编码为(1000)、(0100)、(0010)、(0001),如下所示:

    1. if(0001 & code !=0) {
    2.     x = Xleft;        y = y1+((y2-y1)/(x2-x1))*(Xleft-x1);
    3. } else if(0010 & code != 0) {
    4.     x = Xright;        y = y1+((y2-y1)/(x2-x1))*(Xright-x1);
    5. } else if(0100 & code != 0) {
    6.     y = Ybottom;        x = x1+((x2-x1)/(y2-y1))*(Ybootom-y1);
    7. } else if(0010 & code != 0) {
    8.     y = Ytop;        x = x1+((x2-x1)/(y2-y1))*(Ytop-y1);
    9. }

    具体代码如下所示:

    1. #include
    2. using namespace std;
    3. // 定义9个区域代码
    4. const int INSIDE = 0; // 0000
    5. const int LEFT = 1; // 0001
    6. const int RIGHT = 2; // 0010
    7. const int BOTTOM = 4; // 0100
    8. const int TOP = 8; // 1000
    9. // 定义一个裁剪矩形
    10. const int x_max = 10;
    11. const int y_max = 8;
    12. const int x_min = 4;
    13. const int y_min = 4;
    14. // 对线段端点进行区域编码
    15. int computeCode(double x, double y)
    16. {
    17. // initialized as being inside
    18. int code = INSIDE;
    19. if (x < x_min) // to the left of rectangle
    20. code |= LEFT;
    21. else if (x > x_max) // to the right of rectangle
    22. code |= RIGHT;
    23. if (y < y_min) // below the rectangle
    24. code |= BOTTOM;
    25. else if (y > y_max) // above the rectangle
    26. code |= TOP;
    27. return code;
    28. }
    29. // Cohen-Sutherland算法
    30. // 剪切一条线段 P1 = (x2, y2) to P2 = (x2, y2)
    31. void cohenSutherlandClip(double x1, double y1,
    32. double x2, double y2)
    33. {
    34. // 解析编码
    35. int code1 = computeCode(x1, y1);
    36. int code2 = computeCode(x2, y2);
    37. bool accept = false; // accept为true表示可以显示
    38. while (true) {
    39. if ((code1 == 0) && (code2 == 0)) { // 如果两个端点都在矩形内
    40. accept = true;
    41. break;
    42. }
    43. else if (code1 & code2) { // 完全在窗口外且不相交, 则丢弃该线段
    44. break;
    45. }
    46. else { // 部分线段位于矩形
    47. int code_out;
    48. double x, y;
    49. // 判断哪个端点在矩形外部
    50. if (code1 != 0)
    51. code_out = code1;
    52. else
    53. code_out = code2;
    54. // 公式 y = y1 + 斜率 * (x - x1),
    55. // x = x1 + (1 / 斜率) * (y - y1)
    56. // 对矩形外部的端点进行裁剪
    57. if (code_out & TOP) {
    58. x = x1 + (x2 - x1) * (y_max - y1) / (y2 - y1);
    59. y = y_max;
    60. }
    61. else if (code_out & BOTTOM) {
    62. x = x1 + (x2 - x1) * (y_min - y1) / (y2 - y1);
    63. y = y_min;
    64. }
    65. else if (code_out & RIGHT) {
    66. y = y1 + (y2 - y1) * (x_max - x1) / (x2 - x1);
    67. x = x_max;
    68. }
    69. else if (code_out & LEFT) {
    70. y = y1 + (y2 - y1) * (x_min - x1) / (x2 - x1);
    71. x = x_min;
    72. }
    73. //找到交点x, y, 进行替换,弃掉外部的线段
    74. if (code_out == code1) {
    75. x1 = x;
    76. y1 = y;
    77. code1 = computeCode(x1, y1);
    78. }
    79. else {
    80. x2 = x;
    81. y2 = y;
    82. code2 = computeCode(x2, y2);
    83. }
    84. }
    85. }
    86. if (accept) {
    87. cout << "Line accepted from " << x1 << ", "
    88. << y1 << " to " << x2 << ", " << y2 << endl;
    89. // 用户可以在这里添加代码来显示矩形
    90. }
    91. else
    92. cout << "Line rejected" << endl;
    93. }
    94. int main()
    95. {
    96. // 添加第一条线段 (5, 5),(7, 7)
    97. cohenSutherlandClip(5, 5, 7, 7);
    98. // 添加第一条线段 (7, 9), (11, 4)
    99. // P21 = (7, 9), P22 = (11, 4)
    100. cohenSutherlandClip(7, 9, 11, 4);
    101. // 添加第三条线段 (1, 5), (4, 1)
    102. cohenSutherlandClip(1, 5, 4, 1);
    103. return 0;
    104. }


    小结:

    本算法只支持矩形窗口、如果是其它形状的窗口则不行、
    并且只适合零散的线条裁剪,如果是连续的一条多折线,那么会进行很多重复计算

    下章学习:

    2.qt-Cyrus-Beck算法(凸多边形的线裁剪算法-C++实现)_诺谦的博客-CSDN博客

  • 相关阅读:
    less使用(入门)
    MLIR笔记(3)
    springboot+java+vue.js教室自习室座位预订系统
    嵌入式Linux应用开发-基础知识-第十九章驱动程序基石②
    30_ue4进阶末日生存游戏开发[为道具添加碰撞系统]
    与诈蟹的初次邂逅,你中招了没
    面向对象(高级)
    App的回归测试,有什么高效的测试方法?
    全面升级:监测仪器新规部分解析
    2023/10/25
  • 原文地址:https://blog.csdn.net/qq_37997682/article/details/126428520