二维观察

作者: Gaolex | 来源:发表于2016-04-28 13:13 被阅读292次
二维观察
希望大家喜欢,点赞哦

思维导图的矢量图 访问密码 e9ed

深入讨论在输出设备上显示二维图形的问题

[TOC]

1. 二维观察流水线

  • 观察流水线:将该场景的世界坐标描述经各种处理变换到一个或多个输出设备参照系来显示的过程;
  • 裁剪窗口(世界窗口、观察窗口、viewing window):二维场景中要显示的部分;
  • 视口(viewport):控制在显示窗口的定位;
  • 窗口选择要看什么,而视口指定在输出设备的什么位置进行观察
  • 二维观察变换(two-dimensional viewing transformation)有时称为窗口到视口的变换窗口变换**:场景的描述从二维世界坐标系到设备坐标系的映射;
  • 规范化设备坐标:为了使观察处理独立于输出设备,图形系统将对象描述转化到规范设备坐标系并提供裁剪程序;

世界坐标系的位置首先转到与我们要对场景进行观察所对应的观察坐标系,然后,对象位置变换到该场景的一个二维投影,该投影对应于我们在输出屏幕上看到的结果。然后将该场景存入规范化坐标系(规范化设备坐标系),最后,图形经扫描转换到光栅系统的刷新缓存中进行显示。显示设备的坐标系称为设备坐标系(屏幕坐标系)

2. 裁剪窗口

应用程序要得到特殊的裁剪效果,可通过选择裁剪窗口的不同性质、大小和方向来实现

2.1 观察坐标系裁剪窗口

二维观察变换的一般方法是在世界坐标系中制定一个观察坐标。以该标系为参考通过选定方向和位置来制定矩形裁剪窗口。

二维观察坐标系的确定:

  • 选择世界坐标系的P0=(x0,y0)作为二维观察坐标系的原点;
  • 使用世界坐标系的向量V作为观察坐标系yview轴的方向;(或者根据旋转角度获得观察向上向量)
    变换的步骤:
  • 将观察坐标系原点移动到与世界坐标系原点重合;
  • 旋转观察坐标系使其与世界坐标系重合;
    结果:
    Mwc,vc=R *T

2.2 世界坐标系裁剪窗口

如果要旋转一个二维场景,可以简单地在世界坐标系中将对象旋转(可能有平移)到所需位置并建立裁剪窗口;(步骤同上)

3. 规范化和视口变换

规范化和窗口-视口转换合并成一步/规范化和裁剪在窗口-视口转换之前进行

3.1 裁剪窗口到规范化视口的映射

  1. 以点(xwmin,ywmin)为中心执行缩放变换,将窗口变幻成视口的大小
  2. 将(xwmin,ywmin)移到(xvmin,yvmin
  • 窗口-视口变换保持对象描述的相对位置;
  • 只有在裁剪窗口和视口有相同的纵横比时才能保持对象的相对比例不变
  • 裁剪函数可是有裁剪窗口边界或视口边界实现裁剪

3.2 裁剪窗口到规范化正方形的映射

二维观察的另一种方法是先将裁剪窗口变换到规范正方形,在规范化坐标系中进行裁剪,然后将场景描述变换到屏幕坐标系中指定的视口中

3.3 字符串的显示

  • 保持字符串的大小不变 ==》 点阵字体
  • 对字形轮廓中线段的定义位置进行变换 ==》 轮廓线字体和其他图元

3.4 分画面效果和多输出设备

工作站变换:到所选输出设备的映射
工作站函数

  1. 用于为选定输出设备制定裁剪窗口,用工作站号来标识;
  2. 用来为该设计建立相应的视口

4. OpenGL二维观察函数

GLU库函数提供了制定二维裁剪窗口的函数;GLUT库提供了处理显示窗口的函数。

4.1 OpenGL投影模式

//下列定义裁剪窗口和视口的函数将应用于投影矩阵
glMatrixMode(GL_PROJECTION);//制定投影矩阵作为当前矩阵,它原来设定为单位矩阵
glLoadIdentity();//矩阵重新设定为单位矩阵

4.2 GLU裁剪窗口函数

gluOrtho2D(xwmin,xwmax,ywmin,ywmax);//定义一个二维裁剪窗口、该函数给出了将场景映射到屏幕的正交投影

4.3 OpenGL视口函数

glViewport(xvmin,yvmin,vpWidth,vpHeight);//指定视口参数
glGetIntegerv(GL_VIEWPORT,vpArray);//获取当前活动视口参数的查询函数,在交互式应用中,我们可以使用该函数获得光标所在视口的参数

4.4 建立GLUT显示窗口

//初始化glut
glutInit(&argc,argv);
//定义显示窗口并选择其尺寸及位置
glutInitWindowPosition(xTopLeft,yTopLeft);//位置
glutInitWindowSize(dwWidth,dwHeight);//宽高
glutCreateWindow("Title of Display Window");//标题

4.5 设定GLUT显示窗口的模式和颜色

//显示窗口的参数有下列函数选择
glutInitDisplayMode(mode);/*选择颜色模式(RGB或索引号)和不同的缓存组合,所选参数以逻辑‘或操作方式组合’ 默认模式是但缓存和RGB(或RGBA)颜色模式。eg:glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); */
glClearColor(red,greeb,blue,alpha);//显示窗口的背景颜色(RGB模式)
glClearIndex(index);//显示窗口的背景颜色(颜色索引模式)

4.6 GLUT显示窗口标识

windowID = glutCreatWindow("A Display Window");//记录窗口的标识

4.7 删除GLUT显示窗口

glutDestroyWindow(windowID)

4.8 当前GLUT显示窗口

glutSetWindow(windowID);//指定显示窗口
currentWindowID = glutGetWindow();//询问系统当前显示窗口

4.9 修改GLUT显示窗口的位置和大小

glutPositionWindow(xNewTopLeft,yNewTopLeft);//改变当前显示窗口的位置
glutReshapeWindow(dwNewWidth);//设定当前显示窗口的尺寸
glutFullScreen();//将当前显示窗口扩展到整个屏幕
glutReshapeFunc(winReshapeFun);//调整显示窗口的变化   4.16

4.10 管理多个GLUT显示窗口

glutIconifyWindow();//将当前显示窗口变为一个图符,该图符将使用赋予该窗口的名字来标记
glutSetIconTitle("Icon Name");//改变上面的图符名字
glutSetWindowTitle("New window Name");//改变显示窗口的名字
glutSetWindow(windowID);//制定某个显示窗口为当前窗口
glutPopWindow();//使当前窗口成为所有其他窗口之前的窗口
glutPushWindow();//使当前窗口成为所有其他窗口之后的窗口
glutHideWindow();//让当前窗口从屏幕消失

glutShowWindow();//将隐藏的或变为图符的显示窗口指定为当前显示窗口并调研该函数让它重新显示

4.11 GLUT子窗口

glutCreateSubWindow(windowID,xBottomLeft,yBottomLeft,width,height);//创建子窗口

4.12 显示窗口屏幕光标形状的选择

glutSetCursor(shape);//为当前窗口选择平模光标的形状。shape为符号常量

4.13 在GLUT显示窗口中观察图形对象

glutDisplayFunc(pictureDescrip);//指定当前窗口的显示内容,pictureDescrip是一种回调函数
glutPostRedisplay();//指出当前显示窗口的内容应该更新

4.14 执行应用程序

glutMainLoop();//启动程序执行

4.15 其他GLUT函数

glutIdleFunc(function);//没有其他时间需要处理是可以指定其运行,该函数的参数可以是一个背景函数或更新一个动画参数的过程
glutGet(stateParam);//查询系统某些参数的当前值

4.16 OpenGL的二维观察程序例

#include <GL/glut.h>

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

void init(void)
{
    /*  Set color of display window to white.  */
    //设置背景为白
    glClearColor(1.0, 1.0, 1.0, 0.0);

    /*  Set parameters for world-coordinate clipping window.  */
    glMatrixMode(GL_PROJECTION);//GL_PROJECTION,对投影矩阵应用随后的矩阵操作.
    gluOrtho2D(-100.0, 100.0, -100.0, 100.0);

    /*  Set mode for constructing geometric transformation matrix.  */

    glMatrixMode(GL_MODELVIEW); //GL_MODELVIEW, 对模型视景矩阵堆栈应用随后的矩阵操作.
}

void triangle(wcPt2D *verts)
{
    GLint k;

    glBegin(GL_TRIANGLES);
    for (k = 0; k < 3; k++)
        glVertex2f(verts[k].x, verts[k].y);
    glEnd();
}

void displayFcn(void)
{
    /*  Define initial position for triangle.  */
    wcPt2D verts[3] = { { -50.0, -25.0 },{ 50.0, -25.0 },{ 0.0, 50.0 } };

    glClear(GL_COLOR_BUFFER_BIT);   //  Clear display window.
    //把整个窗口清除为当前的清除颜色
    glColor3f(0.0, 0.0, 1.0);       //  Set fill color to blue.
    
    glViewport(0, 0, 300, 300);     //  Set left viewport.
    /*
    glViewport(GLint x, GLint y, GLsizei width, GLsizei height)为其函数原型。
        X,Y————以像素为单位,指定了视口的左下角(在第一象限内,以(0,0)为原点的)位置。
        width,height————表示这个视口矩形的宽度和高度,根据窗口的实时变化重绘窗口。
    */
    triangle(verts);                //  Display triangle.

                                    /*  Rotate triangle and display in right half of display window.  */
    glColor3f(1.0, 0.0, 0.0);         //  Set fill color to red. 
    glViewport(300, 0, 300, 300);     //  Set right viewport.  
    glRotatef(90.0, 0.0, 0.0, 1.0);   //  Rotate about z axis.  
    //当前坐标系统关于Z轴旋转90度
    triangle(verts);           //  Display red rotated triangle.

    glFlush();//强制刷新缓冲,保证绘图命令将被执行,而不是存储在缓冲区中等待其他的OpenGL命令。
}

void main(int argc, char ** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowPosition(50, 50);
    glutInitWindowSize(600, 300);
    glutCreateWindow("Split-Screen Example");

    init();
    glutDisplayFunc(displayFcn);

    glutMainLoop();
}


5. 裁剪算法

  • 裁剪算法(clipping algorithm):用来消除制定区域内或区域外的图形部分的过程;

  • 裁剪最多应用于观察流水线,目的是为了从场景(二维或三维)中提取制定部分显示在输出设备上;

  • 二维裁剪算法

    • 点的裁剪
    • 线段的裁剪
    • 区域的裁剪(直线段)
    • 曲线的裁剪(多边形)
    • 文字的裁剪
  • 假设裁剪区域是一个正则矩形

6. 二维点裁剪

xwmin =< x =< xwmax
ywmin =< y =< ywmax
若点不满足这四个等式中任何一个,即裁剪掉改点

7. 二维线段裁剪

线段裁剪算法通过一系列的测试和求交计算来判断是否整条线段或其中的某部分可以保存下来

  • 减少交点计算是每一种线段裁剪算法的主要目标

7.1 Cohen-Sutherland线段裁剪算法

该算法通过初始测试来减少交点计算,从而减少线段裁剪算法所用的时间

区域码 窗口及区域编码 裁剪 算法的步骤
通过一个矩形的裁剪区域将整个屏幕分成9个部分,并为每一个部分赋予相应的区域码,然后根据端点的位置确定这个端点的区域码。
先判断能否完全接受或者完全排除一条线段,若以上2个判断无法直接得出,则逐步裁剪,选取一个位于裁剪区外的端点,把端点的区域码和裁剪边界的区域码进行逻辑与运算,若结果为真,则端点在该裁剪边界外部,这时将端点移向线段和该边界的交点处,如此循环,直到裁剪结束。
#include <Windows.h>
#include <gl/glut.h>
//////////////////////////////////////////////////////////////////////////
//区域码
const GLint leftBitCode=0x1;
const GLint rightBitCode=0x2;
const GLint buttonBitCode=0x4;
const GLint topBitCode=0x8;
GLint winWidth=640,winHeight=480;
class screenPT
{
public:
    GLfloat x,y;
};
inline GLint inside(GLint code){return GLint(!code);}   //判断点是否在裁剪区内
inline GLint reject(GLint code1,GLint code2){return GLint(code1&code2);}    //判断能否完全排除一条线段
inline GLint accept(GLint code1,GLint code2){return GLint(!(code1 | code2));}   //判断能否完全接受一条线段
inline void swapPT(screenPT& a,screenPT& b){screenPT t=a;a=b;b=t;}  //交换两个点
inline void swapCode(GLubyte& a,GLubyte& b){GLubyte t=a;a=b;b=t;}   //交换两个区域码
//确定一个点所在位置的区域码
GLubyte encode(const screenPT& p,const screenPT& winMin,const screenPT& winMax)
{
    GLubyte code=0x00;
    if(p.x<winMin.x)
        code |= leftBitCode;
    if(p.x>winMax.x)
        code |= rightBitCode;
    if(p.y<winMin.y)
        code |= buttonBitCode;
    if(p.y>winMax.y)
        code |= topBitCode;
    return code;
}
//在屏幕上画一条未裁剪的线,由裁剪函数调用
void drawOneLine(const screenPT& a,const screenPT& b)
{
    glBegin(GL_LINES);
        glVertex2f(a.x,a.y);
        glVertex2f(b.x,b.y);
    glEnd();
}
//裁剪函数
void lineClip(screenPT winMin,screenPT winMax,screenPT lineBegin,screenPT lineEnd)
{
    GLubyte code1,code2;    //保存两个端点的区域码
    GLboolean done=false,plotLine=false;    //判断裁剪是否结束和是否要绘制直线
    GLfloat k;              //斜率
    while(!done)
    {
        code1 = encode(lineBegin,winMin,winMax);
        code2 = encode(lineEnd,winMin,winMax);
        if(accept(code1,code2))         //当前直线能完全绘制
        {
            done=true;
            plotLine=true;
        }
        else
        {
            if(reject(code1,code2))     //当前直线能完全排除
                done = true;
            else
            {
                if(inside(code1))   //若lineBegin端点在裁剪区内则交换两个端点使它在裁剪区外
                {
                    swapPT(lineBegin,lineEnd);
                    swapCode(code1,code2);
                }
                //计算斜率
                if(lineBegin.x != lineEnd.x)
                    k = (lineEnd.y-lineBegin.y)/(lineEnd.x-lineBegin.x);
                //开始裁剪,以下与运算若结果为真,
                //则lineBegin在边界外,此时将lineBegin移向直线与该边界的交点
                if(code1 & leftBitCode)
                {
                    lineBegin.y += (winMin.x-lineBegin.x)*k;
                    lineBegin.x = winMin.x;
                }
                else if(code1 & rightBitCode)
                {
                    lineBegin.y += (winMax.x-lineBegin.x)*k;
                    lineBegin.x = winMax.x;
                }
                else if(code1 & buttonBitCode)
                {
                    if(lineBegin.x != lineEnd.x)
                        lineBegin.x += (winMin.y-lineBegin.y)/k;
                    lineBegin.y = winMin.y;
                }
                else if(code1 & topBitCode)
                {
                    if(lineBegin.x != lineEnd.x)
                        lineBegin.x += (winMax.y-lineBegin.y)/k;
                    lineBegin.y = winMax.y;
                }
            }
        }
    }
    if(plotLine)
        drawOneLine(lineBegin,lineEnd); //绘制裁剪好的直线
}
//////////////////////////////////////////////////////////////////////////
void rect(screenPT winMin,screenPT winMax)
{
    glBegin(GL_LINE_LOOP);
        glVertex2f(winMin.x,winMin.y);
        glVertex2f(winMax.x,winMin.y);
        glVertex2f(winMax.x,winMax.y);
        glVertex2f(winMin.x,winMax.y);
    glEnd();
}
void init()
{
    glViewport(0,0,winWidth,winHeight);
    glClearColor(1.0,1.0,1.0,0.0);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0,winWidth,0,winHeight);
    glMatrixMode(GL_MODELVIEW);
}
void display()
{
    screenPT winMin,winMax,lineBegin,lineEnd;
    winMin.x=100.0; winMin.y=50.0;
    winMax.x=400.0; winMax.y=300.0;
    lineBegin.x=0.0;    lineBegin.y=0.0;
    lineEnd.x=winWidth; lineEnd.y=winHeight;
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(0.0,0.0,0.0);
    rect(winMin,winMax);    //为裁剪区域绘制一个边框
    lineClip(winMin,winMax,lineBegin,lineEnd);  
    lineBegin.y=240.0;  lineEnd.y=240.0;
    lineClip(winMin,winMax,lineBegin,lineEnd);  
    lineBegin.x=320.0;  lineBegin.y=0.0;
    lineEnd.x=320.0;    lineEnd.y=winHeight;
    lineClip(winMin,winMax,lineBegin,lineEnd);
    glFlush();
}
int main(int argc,char** argv)
{
    glutInit(&argc,argv);
    glutInitWindowPosition(100,100);
    glutInitWindowSize(winWidth,winHeight);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutCreateWindow("my app");
    init();
    glutDisplayFunc(display);
    glutMainLoop();
    return 0;
}

7.2 梁友栋-Barsky 线段裁剪算法

Cyrus和Beck用参数化方法提出了比Cohen-Sutherland更有效的算法。后来梁友栋和Barsky独立地提出了更快的参数化线段裁剪算法,也称为Liany-Barsky(LB)算法。

xmin≤x1+ u·Δx≤xmax

ymin≤y1+ u·Δy≤ymax

这四个不等式可以表示为:u·pk ≤qk , k=1,2,3,4

其中,p、q定义为:

p1=-Δx, q1=x1-xmin
p2= Δx, q2=xmax-x1
p3=-Δy, q3=y1-ymin
p4= Δy, q4=ymax-y1

可得:

任何平行于窗口某边界的直线,其pk=0,k值对应于相应的边界(k=1,2,3,4对应于左、右、下、上边界)。如果还满足qk<0,则线段完全在边界外,应舍弃该线段。如果pk=0并且qk≥0,则线段平行于窗口某边界并在窗口内.
1、当pk<0时,线段从裁剪边界延长线的外部延伸到内部;
2、当pk>0时,线段从裁剪边界延长线的内部延伸到外部;

梁友栋-Barsky 线段裁剪算法实现步骤
1、 初始化线段交点的参数:u1=0,u2=1;
2、 计算出各个裁剪边界的p、q值;
3、 根据p、q来判断:是舍弃线段还是改变交点的参数。
(1) 当p<0时,参数r用于更新u1;      (u1=max{u1,…,rk})
(2) 当p>0时,参数r用于更新u2。      (u2=min{u2,…,rk})
(3) 如果更新了u1或u2后,使u1>u2,则舍弃该线段。
(4) 当p=0且q<0时,因为线段平行于边界并且位于边界之外,则舍弃该线段。见下图所示。
4、 p、q的四个值经判断后,如果该线段未被舍弃,则裁剪线段的端点坐标由参数u1和u2的值决定。

代码:

class wcPt2D 
{
   private:
      GLfloat x, y;
   public:
   /*  Default Constructor: initialize position as (0.0, 0.0).  */
   wcPt3D ( ) {x = y = 0.0;}
   
   setCoords (GLfloat xCoord, GLfloat yCoord) {
      x = xCoord;
      y = yCoord;
   }

   GLfloat getx ( ) const {
      return x;
   }

   GLfloat gety ( ) const {
      return y;
   }
};

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

GLint clipTest (GLfloat p, GLfloat q, GLfloat * u1, GLfloat * u2)
{
    GLfloat r;
    GLint returnValue = true;

    if (p < 0.0) 
    {
        r = q / p;
        if (r > *u2)
            returnValue = false;
        else if (r > *u1)
            *u1 = r;
    }
    else if (p > 0.0) 
    {
        r = q / p;
        if (r < *u1)
            returnValue = false;
        else if (r < *u2)
            *u2 = r;
    }
    else
    /*  Thus p = 0 and line is parallel to clipping boundary.  */
        if (q < 0.0)
        /*  Line is outside clipping boundary.  */
            returnValue = false;

    return (returnValue);
}

void lineClipLiangBarsk (wcPt2D winMin, wcPt2D winMax, wcPt2D p1, wcPt2D p2)
{
    GLfloat u1 = 0.0, u2 = 1.0, dx = p2.getx ( ) - p1.getx ( ), dy;

    if (clipTest (-dx, p1.getx ( ) - winMin.getx ( ), &u1, &u2))
        if (clipTest (dx, winMax.getx ( ) - p1.getx ( ), &u1, &u2)) 
        {
            dy = p2.gety ( ) - p1.gety ( );
            if (clipTest (-dy, p1.gety ( ) - winMin.gety ( ), &u1, &u2))
            if (clipTest (dy, winMax.gety ( ) - p1.gety ( ), &u1, &u2)) 
            {
                if (u2 < 1.0)
                {
                    p2.setCoords (p1.getx ( ) + u2 * dx, p1.gety ( ) + u2 * dy);
                }
            if (u1 > 0.0) 
            {
                p1.setCoords (p1.getx ( ) + u1 * dx, p1.gety ( ) + u1 * dy);
            }
            lineBres (round (p1.getx ( )), round (p1.gety ( )),round (p2.getx ( )), round (p2.gety ( )));
            }
        }
}

示例

梁友栋-Barsky 线段裁剪算法示例 线段的参数方程 结果

梁友栋-Barsky 线段裁剪算法比Cohen-Sutherland线段裁剪算法更有效率,因为减少了交点次数计算

7.3 Nicholl-Lee-Nicholl 线段裁剪算法

  • Cohen-Sutherland线段裁剪算法中,在找到与裁剪矩形边界的焦点之前或者完全舍弃改线段之前,必须对一条线段进行多次求交
  • NLN算法则在求交计算器按进行跟多的区域测试,从而减少求交运算
  • 基本想法:对2D平面的更细的划分

7.4 非矩形多边形裁剪窗口的线段裁剪

  1. 基于参数化直线方程的算法
  2. 对于凹多边形:
  • 将其分解为一组凸多边形再使用基于参数化直线方程的算法
  • 添加一些便使凹裁剪区乘务凸裁剪区域,然后使用修改后的凸多边形组对线段进行一些列裁剪操作。

7.5 非线性裁剪窗口边界的线段裁剪

也可以使用园或者其他期限边界进行裁剪,但使用这些区域的裁剪算法的速度较慢

8. 多边形填充区裁剪

对于多边形,线段裁剪进行处理后的多边形边界显示为不连接的线段

直接采用直线段裁剪的结果 正确的裁剪结果

8.1 Sutherland-Hodgman 多边形裁剪

基本思想:以多边形顶点为初始集合, 首先用窗口左边界剪裁多边形,产生新的顶点序列。新的顶点集依次传给下边界、右边界和上边界进行处理。 SH算法最终输出定义剪裁后的多边形边界的顶点序列

用左边界裁剪 输入:ABCDEFGH 输出:A12DEFGH 用下边界裁剪 输入:A12DEFGH 输出:A134D56FGH 用右边界裁剪 输入:A134D56FGH 输出:A134D5678GH 用上边界裁剪 输入:A134D5678GH 输出:K34D56789IHJ

沿着多边形依次处理顶点的情况
1、 若第一点在窗口边界外、第二点在窗口边界内,将交点I与窗口内的点P输入顶点表

输出I、P

2、 若两顶点都在窗口边界内,只将第二点输入顶点表,如P

输出P

3、若第一点在窗口边界内、第二点在窗口边界外,将交点I输入顶点表

输出I

4、若两点均在窗口边界外,不输入任何点

不输出

代码:

typedef enum { Left, Right, Bottom, Top } Boundary;
const GLint nClip = 4;
//检验某点是否在内部
GLint inside (wcPt2D p, Boundary b, wcPt2D wMin, wcPt2D wMax)
{
    switch (b)
    {
        case Left:   if (p.x < wMin.x) return (false); break;
        case Right:  if (p.x > wMax.x) return (false); break;
        case Bottom: if (p.y < wMin.y) return (false); break;
        case Top:    if (p.y > wMax.y) return (false); break;
    }
    return (true);
}
//检验线段是否与边界相交
GLint cross (wcPt2D p1, wcPt2D p2, Boundary winEdge, wcPt2D wMin, wcPt2D wMax)
{
    if (inside (p1, winEdge, wMin, wMax) == inside (p2, winEdge, wMin, wMax))
        return (false);
    else 
        return (true);
}
//intersect(vt.  横断,横切,横穿;vt.& vi.  (指线条、道路等)相交,交叉;)
wcPt2D intersect (wcPt2D p1, wcPt2D p2, Boundary winEdge, wcPt2D wMin, wcPt2D wMax)
{
    wcPt2D iPt;
    GLfloat m;

    if (p1.x != p2.x) 
        m = (p1.y - p2.y) / (p1.x - p2.x);
    switch (winEdge) 
    {
        case Left:
            iPt.x = wMin.x;
            iPt.y = p2.y + (wMin.x - p2.x) * m;
            break;
        case Right:
            iPt.x = wMax.x;
            iPt.y = p2.y + (wMax.x - p2.x) * m;
            break;
        case Bottom:
            iPt.y = wMin.y;
            if (p1.x != p2.x) iPt.x = p2.x + (wMin.y - p2.y) / m;
            else iPt.x = p2.x;
            break;
        case Top:
            iPt.y = wMax.y;
            if (p1.x != p2.x) iPt.x = p2.x + (wMax.y - p2.y) / m;
            else iPt.x = p2.x;
            break;
    }

    return (iPt);
}

void clipPoint (wcPt2D p, Boundary winEdge, wcPt2D wMin, wcPt2D wMax,wcPt2D * pOut, int * cnt, wcPt2D * first[], wcPt2D * s)
{
    wcPt2D iPt;

    /* If no previous point exists for this clipping boundary,
    * save this point.
    */
    if (!first[winEdge])
        first[winEdge] = &p;
    else
        /*  Previous point exists.  If p and previous point cross
        *  this clipping boundary, find intersection.  Clip against
        *  next boundary, if any.  If no more clip boundaries, add
        *  intersection to output list.  
        */
        if (cross (p, s[winEdge], winEdge, wMin, wMax))
        {
            iPt = intersect (p, s[winEdge], winEdge, wMin, wMax);
            if (winEdge < Top)
                clipPoint (iPt, b+1, wMin, wMax, pOut, cnt, first, s);
                else 
                {
                    pOut[*cnt] = iPt;  (*cnt)++; 
                }
        }

    /*  Save p as most recent point for this clip boundary.  */
    s[winEdge] = p;  

    /*  For all, if point inside, proceed to next boundary, if any.  */
    if (inside (p, winEdge, wMin, wMax))
        if (winEdge < Top)
            clipPoint (p, winEdge + 1, wMin, wMax, pOut, cnt, first, s);
        else 
        {
            pOut[*cnt] = p;  (*cnt)++;
        }
}

void closeClip (wcPt2D wMin, wcPt2D wMax, wcPt2D * pOut,GLint * cnt, wcPt2D * first [ ], wcPt2D * s)
{
    wcPt2D pt;
    Boundary winEdge;

    for (winEdge = Left; winEdge <= Top; winEdge++)
    {
        if (cross (s[winEdge], *first[winEdge], winEdge, wMin, wMax)) 
        {
            pt = intersect (s[winEdge], *first[winEdge], winEdge, wMin, wMax);
            if (winEdge < Top)
                clipPoint (pt, winEdge + 1, wMin, wMax, pOut, cnt, first, s);
            else 
            {
                pOut[*cnt] = pt;  (*cnt)++;
            }
        }
    }
}

GLint polygonClipSuthHodg (wcPt2D wMin, wcPt2D wMax, GLint n, wcPt2D * pIn, wcPt2D * pOut)
{
    /*  Parameter "first" holds pointer to first point processed for 
    *  a boundary; "s" holds most recent point processed for boundary.  
    */
    wcPt2D * first[nClip] = { 0, 0, 0, 0 }, s[nClip];//指针数组
    GLint k, cnt = 0;

    for (k = 0; k < n; k++)
        clipPoint (pIn[k], Left, wMin, wMax, pOut, &cnt, first, s);

    closeClip (wMin, wMax, pOut, &cnt, first, s);
    return (cnt);
}

缺点:对凹多边形裁剪会出现多余的线

8.2 Weiler-Atherton多边形裁剪

基本思想:有时延多边形某一条边的方向来处理顶点,有时沿窗口的边界方向处理。一般按逆时针(顺时针)以及当前处理多边形顶点对是由外到内还是由内到外

裁剪前 裁剪后

8.3 非矩形的多边形窗口的多边形裁剪

  1. 梁友栋-Barsky 线段裁剪算法
  2. Weiler-Atherton多边形裁剪

8.4 非线性裁剪窗口边界的多边形裁剪

  1. 先用直线逼近边界,然后使用一般的多边形裁剪窗口算法对其进行处理
  2. 使用线段裁剪中讨论的通用算法

9. 曲线的裁剪

使用类似上一节的方法进行裁剪

10. 文字的裁剪

  1. 全部保留或者全部舍弃字符串的裁剪策略,速度最快


    裁剪前
裁剪后
  1. 全部保留或者舍弃字符
裁剪前 裁剪后
  1. 裁剪单个字符的组成部分
裁剪前 裁剪后

11. 小结

谢谢观看
希望大家喜欢,点赞哦

相关文章

  • 图形学复习知识点2

    二维几何变换(待补) 平移 二维观察流水线 C代表coordinate建模坐标系-> 世界坐标系-> 观察坐标系-...

  • 二维观察

    思维导图的矢量图 访问密码 e9ed 深入讨论在输出设备上显示二维图形的问题 [TOC] 1. 二维观察流水线...

  • 非暴力沟通3

    第一要素:观察(觉知) 要区分观察和评论, 并且不要用静态的语言捕捉变动的现实,不要用线性思维,在二维世界里看现象...

  • 05 中轴线|硬笔临小楷|《灵飞经中的简体字》结构解读

    引言: 对称性是二维结构保持平衡的基础。观察一个建筑,除了从上到下去欣赏,观察它的平衡性也是必要的角度。 汉字和建...

  • 06 左右呼应|硬笔临小楷|《灵飞经中的简体字》结构解读

    引言: 对称性是二维结构保持平衡的基础。观察一个建筑,除了从上到下去欣赏,从左右视角观察它的平衡性也是必要工作,汉...

  • openGL——02、二维观察流水线

    理解视口viewport函数 使用opengl实现如下图所示的二维螺旋线,但只能有一个drawSpiralLine...

  • 读三体有感

    《三体》 印象最深的莫过于维度的概念和黑暗森林法则,是否存在四维生物观察我们三维世界像我们观察一张二维平面图一样呢...

  • 超弦理论和M理论

    我们观察一个二维曲面,感觉很容易弄明白它的性质,其原因就在于我们是在三维空间中观察。你可能还记得,狭义相对论中看上...

  • 矩阵的二维变换(转)

    二维平移、二维旋转 二维缩放、通用二维基准点旋转矩阵 通用二维基准点缩放矩阵、通用二维定向缩放矩阵、通用二维复合变...

  • 二维变换

    二维平移、二维旋转 二维缩放、通用二维基准点旋转矩阵 通用二维基准点缩放矩阵、通用二维定向缩放矩阵、通用二维复合变...

网友评论

    本文标题:二维观察

    本文链接:https://www.haomeiwen.com/subject/xnqhrttx.html