第二章 二维图元的生成 ( 第二部分: 圆与椭圆的扫描转换)

第二章 二维图元的生成

  • 掌握扫描转换直线段的DDA算法、中点算法

    • 及中点算法在哪些方面对DDA算法做了改进
  • 掌握扫描转换直线段的 Bresenham算法

  • 掌握圆弧的八对称性, 扫描转换圆弧的中点算法

  • 掌握生成圆弧的多边形迫近法

  • 了解正负法,掌握怎样利用正负法生成圆弧

  • 掌握扫描转换椭圆弧的中点算法

  • 了解线画图元的属性(线型、线宽)控制方法

圆弧的扫描转换

处理对象为圆心在原点的圆弧, 如果不在就使用坐标变换
由于圆的 八对称性, 只需要扫描转换八分之一圆弧就可以了

void CirclePoints(int x, int y, int color) 
{
    Putpixel(x, y, color);
    Putpixel(y, x, color);
    
    Putpixel(-x, y, color);
    Putpixel(y, -x, color);
    
    Putpixel(x, -y, color);
    Putpixel(-y, x, color);
    
    Putpixel(-x, -y, color);
    Putpixel(-y, -x, color);
}

直接离散的圆弧转换方法

缺点

  • 需要开根号/三角函数运算, 计算量大
  1. 离散点

在这里插入图片描述

  1. 离散角度

在这里插入图片描述

圆弧的正负划分性

圆弧的隐函数
F ( x , y ) = X 2 + Y 2 − R 2 = 0 F(x,y)=X^2+Y^2-R^2=0 F(x,y)=X2+Y2R2=0
对于圆内的点 F(x,y) < 0

对于圆外的点 F(x,y) > 0

生成圆弧的中点算法

假设, 已知

  • 半径 R
  • 圆心在原点

只考虑第一象限上侧的 1/8 圆, 则切线的斜率 k 属于 [1, 0]

则点 (xi, yi) 的取整值 (xi, yi') 只有两个取值, 即图中的点 ESE

在这里插入图片描述

把中点 M(X_i+1, y_i+1) 带入隐函数

  1. F(M) < 0, 则 M 在圆内, 说明点 E 更近, 取 E
  2. F(M) > 0, 则 M 在圆外, 取 SE

算法步骤

构造判别式
d = F ( M ) = F ( X i + 1 , Y i − 0.5 ) = ( X i + 1 ) 2 + ( Y i − 0.5 ) 2 + R 2 d=F(M)=F(X_i+1,Y_i-0.5)=(X_i+1)^2+(Y_i-0.5)^2+R^2 d=F(M)=F(Xi+1,Yi0.5)=(Xi+1)2+(Yi0.5)2+R2

  1. d < 0, 取点 E, 下一个像素判别式为

d 1 = F ( X i + 2 , Y i − 0.5 ) = d + 2 X i + 3 d1=F(X_i+2,Y_i-0.5)=d+2X_i+3 d1=F(Xi+2,Yi0.5)=d+2Xi+3

d1 的增量为
Δ d 1 = 2 X i + 3 \Delta d1=2X_i+3 Δd1=2Xi+3

  1. d >= 0, 取点 SE, 下一个像素判别式为

d 2 = F ( X i + 2 , Y i − 1.5 ) = d + ( 2 X i + 3 ) + ( − 2 Y i + 2 ) d2=F(X_i+2,Y_i-1.5)=d+(2X_i+3)+(-2Y_i+2) d2=F(Xi+2,Yi1.5)=d+(2Xi+3)+(2Yi+2)

d2 的增量为
Δ d 2 = 2 ( X i − Y i ) + 5 \Delta d2=2(X_i-Y_i)+5 Δd2=2(XiYi)+5
d 的初值为 d0 = F(1,R-0.5) = 1.25-R, 因为已知第一个像素为 (0,R)

优化浮点数

由于 d 是浮点数, 用 e = d-0.25 代替

初始值: e0 = 1-R

判别式: d < 0 对应 e < 0.25, 但由于初始值 e0 和增量都为整数, e 始终为整数,所以 e < 0.25 等价于 e < 0

void MidPointCircle(int r, int color)
{
    int x, y, d;
    x = 0;
    y = r;
    d = 1 - r;                    			// 初值 d = 1 - r
    Circlepoints(x, y, color);           	// 画八分对称性的其他点

    while (x <= y)                       	// 画到直线 x = y 为止
    {
        if (d < 0)  
            d += 2 * x + 3;              	// d < 0,取右侧点,d 增
        else 
        {
            d += 2 * (x - y) + 5;        	// d >= 0,取右下点,d 增
            y--;
        }
        x++;
        Circlepoints(x, y, color);       	// 画八分对称性的其他点
    }
}

生成圆弧的多边形逼近法
圆的正内接多边形迫近法

假设多边形的第一个顶点 (x0,y0) 位于圆周上的某一点,通常选择 (R, 0)

从第一个顶点开始,通过旋转角度 α = 2π/n,依次计算出后续的顶点坐标

这个旋转变换可以看作是围绕原点旋转角度 α 的操作,使用标准的二维旋转矩阵即可:

( x i + 1 y i + 1 ) = ( cos ⁡ α − sin ⁡ α sin ⁡ α cos ⁡ α ) ( x i y i ) \begin{pmatrix} x_{i+1} \\ y_{i+1} \end{pmatrix}= \begin{pmatrix} \cos \alpha & -\sin \alpha \\ \sin \alpha & \cos \alpha \end{pmatrix} \begin{pmatrix} x_{i} \\ y_{i} \end{pmatrix} (xi+1yi+1)=(cosαsinαsinαcosα)(xiyi)

因为 α 是个常数, 每个顶点只需要计算 4 次乘法, n边型就需要计算 4n 次乘法, 再加上直线段的中点算法的计算量

问题: 给定最大逼近误差(最大距离) Δ,如何确定多边形的边数 n 或角度 α

在这里插入图片描述

即要求:

d = R − R cos ⁡ ( α 2 ) ≤ Δ d = R - R \cos\left(\frac{\alpha}{2}\right) \leq \Delta d=RRcos(2α)Δ
由此可得:

α ≤ 2 arccos ⁡ ( R − Δ R ) \alpha \leq 2 \arccos\left(\frac{R - \Delta}{R}\right) α2arccos(RRΔ)
边数 n 满足

n ≥ 360 α n \geq\frac{360}{\alpha} nα360

圆的等面积正多边形迫近法

有多种方法, 这里介绍一种

  1. 根据给定的最大逼近误差求出多边形边的圆心角 α
  2. 根据下图示意, 求出边 OPi 的长度
  3. 旋转变换得到其他顶点

在这里插入图片描述

OP 的过程

这里为了简化公式, 令 OP = r, 圆的半径为 R
A sector = 1 2 R 2 α = A triangle = 1 2 r 2 ⋅ sin ⁡ ( α ) A_{\text{sector}} = \frac{1}{2} R^2 \alpha= A_{\text{triangle}} = \frac{1}{2} r^2 \cdot \sin(\alpha) Asector=21R2α=Atriangle=21r2sin(α)

r = R α sin ⁡ ( α ) r = R \sqrt{\frac{\alpha}{\sin(\alpha)}} r=Rsin(α)α

椭圆的扫描转换(生成椭圆的中点算法)

类似于圆弧的扫描转换, 但由于椭圆只具有四对称性, 所以考虑将第一象限的圆弧分为上下两部分分别绘制, 以 k = -1 为分界线

椭圆的隐函数
F ( x , y ) = b 2 x 2 + a 2 y 2 − a 2 b 2 = 0 F(x,y)=b^2x^2+a^2y^2-a^2b^2=0 F(x,y)=b2x2+a2y2a2b2=0
对于椭圆内的点 F(x,y) < 0

对于椭圆外的点 F(x,y) > 0

椭圆上一点处的法向如下公式, 可以用椭圆的隐函数求偏导推出, 这里不做详细解释
N ( x , y ) = 2 b 2 x i + 2 a 2 y i N(x,y)=2b^2x_i+2a^2y_i N(x,y)=2b2xi+2a2yi

如何判断从上段进入下段

在上半部分, 法向量的 x分量 < y分量, 而下半部分则相反

因此, 当不等式改变方向时, 椭圆就从上段进入了下段

算法步骤

上段部分

因为斜率在 [-1,0], 所以求 x 递增 1 时, y 递减多少

当前像素的下一个像素可能是 点 或 右下

构造判别式
d = F ( x i + 1 , y i − 0.5 ) = b 2 ( x i + 1 ) 2 + a 2 ( y i − 0.5 ) 2 − a 2 b 2 d=F(x_i+1,y_i-0.5)= b^2(x_i+1)^2+a^2(y_i-0.5)^2-a^2b^2 d=F(xi+1,yi0.5)=b2(xi+1)2+a2(yi0.5)2a2b2

  1. d < 0, 取右点, 下一个像素判别式为

d 1 = F ( X i + 2 , Y i − 0.5 ) = d + b 2 ( 2 X i + 3 ) d1=F(X_i+2,Y_i-0.5)=d+b^2(2X_i+3) d1=F(Xi+2,Yi0.5)=d+b2(2Xi+3)

d1 的增量为
Δ d 1 = b 2 ( 2 X i + 3 ) \Delta d1=b^2(2X_i+3) Δd1=b2(2Xi+3)

  1. d >= 0, 取右下点, 下一个像素判别式为

d 2 = F ( X i + 2 , Y i − 1.5 ) = d + b 2 ( 2 X i + 3 ) + a 2 ( − 2 Y i + 2 ) d2=F(X_i+2,Y_i-1.5)=d+b^2(2X_i+3)+a^2(-2Y_i+2) d2=F(Xi+2,Yi1.5)=d+b2(2Xi+3)+a2(2Yi+2)

d2 的增量为
Δ d 2 = b 2 ( 2 X i + 3 ) + a 2 ( − 2 Y i + 2 ) \Delta d2=b^2(2X_i+3)+a^2(-2Y_i+2) Δd2=b2(2Xi+3)+a2(2Yi+2)
d 的初值为
d 0 = F ( 1 , b − 0.5 ) = b ∗ b + a ∗ a ( − b + 0.25 ) d0 = F(1,b-0.5) = b*b+a*a(-b+0.25) d0=F(1,b0.5)=bb+aa(b+0.25)

下段部分

在这里插入图片描述

因为斜率在 [-∞,-1], 所以求 y 递减 1 时, x 递增多少

当前像素的下一个像素可能是 点 或 右下

构造判别式, 为防止混淆, 这里使用 e
e = F ( x i + 0.5 , y i − 1 ) = b 2 ( x i + 0.5 ) 2 + a 2 ( y i − 1 ) 2 − a 2 b 2 e=F(x_i+0.5,y_i-1)= b^2(x_i+0.5)^2+a^2(y_i-1)^2-a^2b^2 e=F(xi+0.5,yi1)=b2(xi+0.5)2+a2(yi1)2a2b2

  1. e >= 0, 中点在椭圆弧外, 取下点, 下一个像素判别式为

e 1 = F ( X i + 0.5 , Y i − 2 ) = d + a 2 ( − 2 Y i + 3 ) e1=F(X_i+0.5,Y_i-2)=d+a^2(-2Y_i+3) e1=F(Xi+0.5,Yi2)=d+a2(2Yi+3)

e1 的增量为
Δ e 1 = a 2 ( − 2 y i + 3 ) \Delta e1=a^2(-2y_i+3) Δe1=a2(2yi+3)

  1. e < 0, 中点在椭圆弧内, 取右下点, 下一个像素判别式为

e 2 = F ( X i + 1.5 , Y i − 2 ) = d + b 2 ( 2 X i + 2 ) + a 2 ( − 2 Y i + 3 ) e2=F(X_i+1.5,Y_i-2)=d+b^2(2X_i+2)+a^2(-2Y_i+3) e2=F(Xi+1.5,Yi2)=d+b2(2Xi+2)+a2(2Yi+3)

e2 的增量为
Δ e 2 = b 2 ( 2 X i + 2 ) + a 2 ( − 2 Y i + 3 ) \Delta e2=b^2(2X_i+2)+a^2(-2Y_i+3) Δe2=b2(2Xi+2)+a2(2Yi+3)
e 的初值为
e 0 = F ( x p + 0.5 , y p − 1 ) , 其中 ( x p , y p ) 是上半段结束时的坐标 e0 = F(x_p+0.5,y_p-1), 其中(x_p,y_p)是上半段结束时的坐标 e0=F(xp+0.5,yp1),其中(xp,yp)是上半段结束时的坐标
y = 0 时, 算法终止

void MidpointEllipse(int a, int b, int color)
{
    int x, y;
    float d1, d2;
    
    x = 0;
    y = b;
    
    d1 = b * b + a * a * (-b + 0.25);  									// d1初值
    putpixel(x, y, color);
    
    // 上半部分
    while (b * b * (x + 1) < a * a * (y - 0.5))  						// 直到上下分界点
    {
        if (d1 < 0) {
            d1 += b * b * (2 * x + 3);  								// 取右点, d1增
            x++;
        } else {
            d1 += (b * b * (2 * x + 3) + a * a * (-2 * y + 2));			// 取右下点,d1增
            x++;
            y--;
        }
        EllipsePoints(x, y, color); 									// 四对称画点
    }
    
    // 下半部分
    d2 = b * b * (x + 0.5) * (x + 0.5) + a * a * (y - 1) * (y - 1) - a * a * b * b;	// d2初值
    while (y > 0)  																	// 直到x坐标轴
    {
        if (d2 < 0) {
            d2 += b * b * (2 * x + 2) + a * a * (-2 * y + 3);  						// 取右下点
            x++;
            y--;
        } else {
            d2 += a * a * (-2 * y + 3);  											// 取下点
            y--;
        }
        EllipsePoints(x, y, color);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值