首页 > 编程语言 >线段裁剪:Cohen-Sutherland算法

线段裁剪:Cohen-Sutherland算法

时间:2023-10-01 18:11:59浏览次数:43  
标签:p1 边界 GLint 线段 裁剪 矩形 Sutherland Cohen

目录

裁剪算法

计算机内部存储的图形数据量通常较大,而屏幕只显示其中一部分,因此需要确定哪些部分在显示区域内,哪些在显示区域外。这个过程称为裁剪(clipping)。裁剪是二维观察(三维观察)的重要部分,参见计算机图形:二维观察

点裁剪:只需要判断点与正则矩形边界的位置关系。如果落在裁剪矩形内,保留;如果落在裁剪矩形外,则去掉。

直线段裁剪:需要判断线段与裁剪矩形的位置关系,有多种算法:
1)Cohen-Sutherland算法;
2)梁有栋-Barsky算法;
3)Nicholl-Lee-Nicholl算法;


Cohen-Sutherland线段裁剪算法

基本思想

用裁剪窗口对应的正则矩形区域,按边界将平面划分为9部分,每个部分对应一个区域码。通过为线段端点建立区域码,快速判断端点与裁剪矩形的位置关系。

  1. 当线段在矩形内部时,全部保留;
  2. 当线段在矩形外部时,全部裁剪掉;
  3. 当线段与裁剪矩形相交时,求出交点,并裁剪掉矩形外的部分,保留矩形内的线段。最终得到裁剪后的线段。

tips:正则矩形,是指边界与x轴或y轴平行的矩形。

具体步骤

  1. 对矩形边界划分空间得到9个部分均进行编码,从而得到9个区域码:

每个区域码用一个4bit二进制数表示,其含义:

  • 0000 裁剪窗口内部;
  • 1001 裁剪窗口左上;
  • 0001 裁剪窗口正左侧;
  • 0101 裁剪窗口左下;
  • 1000 裁剪窗口正上方;
  • 0100 裁剪窗口正下方;
  • 1010 裁剪窗口右上;
  • 0010 裁剪窗口正右侧;
  • 0110 裁剪窗口右下。

区域码的4个bit位的毎一位都代表一个矩形边界:

  1. 为线段端点建立区域码

要判断线段与矩形位置关系,先判断端点与矩形的关系,为每个端点建立区域码(含4bit有效区域码)。有2种方法:
Ⅰ:比较法
1)如果\(x<xw_{min}\),则bit1 = 1;
2)如果\(x>xw_{max}\),则bit2 = 1;
3)如果\(y<yw_{min}\),则bit3 = 1;
4)如果\(y>yw_{max}\),则bit4 = 1。

Ⅱ:取符号位法
1)计算端点坐标与矩形边界的差;
2)用差值设置区域码对应值,bit1为\(x-xw_{min}\)符号位,bit2为\(xw_{max}-x\)符号位,bit3为\(y-yw_{min}符号位\),bit4为\(yw_{max}-y符号位\)。

  1. 判断线段与裁剪矩形的位置关系

为线段2端点建立区域码后,就能快速判断与裁剪窗口的位置关系。
1)矩形内:进行逻辑或操作。如果2个区域码均为0000,则线段完全在矩形内,需保留;
2)矩形外:进行逻辑与操作。如果有一对相同位置都为1,则线段位于矩形之外,需丢弃;
3)相交:线段与矩形边界相交,但不一定与矩形相交,需要进一步测试线段与其他边界交点。可能需要多次求交运算完成线段裁剪,每次处理一条边界后,裁掉其中一部分,其余部分对照窗口其余边界进行检查。

  1. 逐边界对相交线段进行裁剪

假定矩形裁剪窗口边界处理顺序:左、右、下、上,即bit1->bit4。要检查线段是否与某个裁剪矩形边界相交,可以检查2个端点区域码对应bit位:如果一个是1,另一个0,则线段与该边界相交。然而,线段与矩形相交的部分,还需要进一步判断。下面给出具体过程:

如下图,线段P1P2、P3P4均与左边界相交,但P1P2与矩形相交,而P3P4在矩形外,

对于线段P1P2,端点P1、P2区域码分别为0100、1001。按裁剪窗口边界顺序处理(左右下上)

1)检查左边界:P1在左边界内,P2在外。
2)计算出与左边界交点P2',并裁剪掉左边界外的部分P2'~P2。
3)检查右边界:位于右边界内部。
4)检查下边界:P2'在下边界内,P1在外。
5)计算出与下边界交点P1',并裁剪掉下边界外的部分P1'~P1。
6)检查上边界:P2'再上边界外,P1'在内。
7)计算出与上边界交点P2'',并裁减掉上边界外的部分P2'~P2''。

对于线段P3P4,P3在左边界外P4在内(区域码bit1),与左边界交点P3',裁剪掉左边界外的部分P3P3';而P3'P4在下边界外,因此都裁剪掉。

  • 如何求线段与边界交点?
    将边界坐标值(\(x=xw_{min}或xw_{max},y=yw_{min}或yw_{max}\)),代入直线的点斜式:

\[\tag{1} y = y_0+m(x-x_0) \]

计算分析

对区域进行编码,涉及到比较/位运算。
求线段与边界交点时,用到了点斜式,需要先计算直线斜率,涉及到浮点数乘除法运算。因为有4个边界,所以最多需要求4次交点。

程序代码

指定裁剪矩形,对线段裁剪后,绘制线段。

class wcPt2D {
public:
       GLfloat x, y;
};

inline GLint round(const GLfloat a)
{
       return (GLint)(a + 0.5);
}

// APP窗口尺寸
GLint winWidth = 600, winHeight = 500;
// 边界区域码
const GLint winLeftBitCode   = 0x1;
const GLint winRightBitCode  = 0x2;
const GLint winBottomBitCode = 0x4;
const GLint winTopBitCode    = 0x8;

// 根据区域码判断裁剪点是否位于裁剪矩形内
// 返回值设置成bool 更合适
inline GLint inside(GLint code)
{
       return (GLint)(!code);
}

// 判断2个区域码是否位于裁剪矩形外
inline GLint reject(GLint code1, GLint code2)
{
       return code1 & code2;
}

// 判断2个区域码是否位于裁剪矩形内
inline GLint accept(GLint code1, GLint code2)
{
       return (GLint)!(code1 | code2);
}

// 计算裁剪点pt的区域码
// winMin, winMax包含裁剪区域信息
GLubyte encode(wcPt2D pt, wcPt2D winMin, wcPt2D winMax)
{
       GLubyte code = 0x00;

       if (pt.x < winMin.x)
              code = code | winLeftBitCode;
       if (pt.x > winMax.x)
              code = code | winRightBitCode;
       if (pt.y < winMin.y)
              code = code | winBottomBitCode;
       if (pt.y > winMax.y)
              code = code | winTopBitCode;

       return code;
}

void swapPts(wcPt2D* p1, wcPt2D* p2)
{
       wcPt2D tmp;

       tmp = *p1; 
       *p1 = *p2; 
       *p2 = tmp;
}

void swapCode(GLubyte* c1, GLubyte* c2)
{
       GLubyte tmp;

       tmp = *c1;
       *c1 = *c2;
       *c2 = tmp;
}

void lineClipCohSuth(wcPt2D winMin, wcPt2D winMax, wcPt2D p1, wcPt2D p2)
{
       GLubyte code1, code2;
       GLboolean done = false, plotLine = false;
       GLfloat m = 0.0;

       while (!done) {
              // 为线段2端点建立区域码
              code1 = encode(p1, winMin, winMax);
              code2 = encode(p2, winMin, winMax);

              if (accept(code1, code2)) { // 线段在矩形内
                     done = true;
                     plotLine = true;
              }
              else if (reject(code1, code2)) { // 线段在矩形外
                     done = true;
              }
              else { // 线段与矩形相交或在矩形外, 需进一步测试
                     /* Label the endpoint outside the display window as p1. */
                     if (inside(code1)) { // 确保在边界外的点是p1, 那么在边界内的是p2
                           swapPts(&p1, &p2);
                           swapCode(&code1, &code2);
                     }

                     /* Use slope m to find line-clipEdge intersection. */
                     if (p2.x != p1.x) {
                           m = (p2.y - p1.y) / (p2.x - p1.x); // 斜率
                     }

                     // 按左->右->下->上的顺序, 测试边界是否裁剪后线段相交
                     if (code1 & winLeftBitCode) { // 与左边界相交, 更新p1到左边界
                           // 与左边界相交, 则斜率存在
                           p1.y += (winMin.x - p1.x) * m; // y - y0 = (x - x0)m
                           p1.x = winMin.x;
                     }
                     else if (code1 & winRightBitCode) { // 与右边界相交, 更新p1到右边界
                           // 与右边界相交, 则斜率存在
                           p1.y += (winMax.x - p1.x) * m; // y - y0 = (x - x0)m
                           p1.x = winMax.x;
                     }
                     else if (code1 & winBottomBitCode) { // 与下边界相交, 更新p1到下边界
                           /* Need to update p1.x for nonvertical lines only */
                           if (p2.x != p1.x) {
                                  p1.x += (winMin.y - p1.y) / m; // x - x0 = (y - y0)/m
                           }
                           p1.y = winMin.y;
                     }
                     else if (code1 & winTopBitCode) { // 与上边界相交, 更新p1到下边界
                           if (p2.x != p1.x) {
                                  p1.x += (winMax.y - p1.y) / m; // x - x0 = 
(y-y0)/m
                           }
                           p1.y = winMax.y;
                     }
              }
       }

       // 绘制裁剪后的线段p1p2
       if (plotLine) {
              glBegin(GL_LINES);
              glVertex2i(round(p1.x), round(p1.y));
              glVertex2i(round(p2.x), round(p2.y));
              glEnd();
       }
}

标签:p1,边界,GLint,线段,裁剪,矩形,Sutherland,Cohen
From: https://www.cnblogs.com/fortunely/p/17739080.html

相关文章

  • Linux内核裁剪移植
    目录前奏内核移植步骤制作可引导的LinuxISO文件对新机器配置网络前奏rambo@debian:~$cat/etc/issueDebianGNU/Linux12\n\lrambo@debian:~$free-htotalusedfreesharedbuff/cacheavailableMem:15Gi1.......
  • ScrollView如何裁剪粒子特效
    1)ScrollView如何裁剪粒子特效2)Unreal在移动设备中无法使用Stat命令获取到GPUThread的耗时3)Unity中如何看到相机视野范围内的剔除结果这是第354篇UWA技术知识分享的推送,精选了UWA社区的热门话题,涵盖了UWA问答、社区帖子等技术知识点,助力大家更全面地掌握和学习。UWA社区主页:com......
  • 依照遥感影像的像元数量(行数与列数)裁剪图像:ENVI操作
      本文介绍基于ENVI软件,实现栅格遥感影像按照像元行列号与个数进行指定矩形区域裁剪的方法。  一般的,如果我们需要裁剪某个具体的行政区域,按照对应区域的矢量图层裁剪即可;如果需要裁剪某个大致的区域范围,可以按照ArcMap如何手动绘制矢量点、线、面要素?中的方法手动划定区域并......
  • ffmpeg裁剪视频
    ffmpeg命令格式:usage:ffmpeg[options][[infileoptions]-iinfile]...{[outfileoptions]outfile}...crop的参数格式为w:h:x:y (注意,宽:高写在前面)w:h为输出视频的宽和高,x:y标记输入视频中的某点,将该点作为基准点,向右下进行裁剪得到输出视频。如果xy不写的话,默......
  • ArcPy用矢量数据的空间范围裁剪多张遥感影像
      本文介绍基于Python中ArcPy模块,基于矢量数据范围,对大量栅格遥感影像加以批量裁剪掩膜的方法。  首先,话不多说,本文所需要的代码如下所示。#-*-coding:utf-8-*-"""CreatedonTueDec1320:07:482022@author:fkxxgis"""importarcpyfromarcpy.saimport*t......
  • 关于Lua ClippingNode裁剪
    关于裁剪,这个我有遇到一个坑(3.4)的,在做裁剪的时候,整个背景色都是灰色,没有裁剪效果,C++上是可以正常运行,显示裁剪效果,但是用Lua就没有,我找了半天也没有找到原因,。最后在一片文章中看到了这样一句话:需要在初始化app的时候,添加如下代码:_app=newAppDelegate();<spanstyle="color:#......
  • cocos2dx 3.x ClippingNode裁剪图形
    autoclipNode=ClippingNode::create(); clipNode->setInverted(true); clipNode->setAlphaThreshold(0.0f); this->addChild(clipNode,100); autonewLayer=LayerColor::create(Color4B(0,0,0,150));//添加灰色层 clipNode->addChild(newLayer); au......
  • VTK 实例67:裁剪
    1#include<vtkAutoInit.h>2VTK_MODULE_INIT(vtkRenderingOpenGL2);3VTK_MODULE_INIT(vtkRenderingVolumeOpenGL2);4VTK_MODULE_INIT(vtkRenderingFreeType);5VTK_MODULE_INIT(vtkInteractionStyle);678#include<vtkSmartPointer.h&......
  • 【Python&RS】基于GDAL遥感影像分幅裁剪
    ​    随着科技的进步,遥感影像包含的信息越来越多,存储空间也变得很大,这就导致我们在处理影像时软件会非常的卡。同时目前很火的深度学习也需要对影像分割后制作样本集,所以今天给大家分享下如何使用Python的GDAL库对遥感影像进行分幅裁剪!一、导入需要的三方库   ......
  • Python PIL Image.crop()详解+裁剪四元组定位的小技巧
    0Image.crop详解image.crop是Python中用于裁剪图片的函数。在使用该函数前,我们需要先导入PIL库,即PythonImageLibrary。fromPILimportImage#打开图片img=Image.open('example.jpg')#图片的裁剪区域(区域左上角的坐标为(100,100),右下角的坐标为(300,300))crop_are......