欢迎来到今天的讨论,我们将探讨,学而思编程怎么打开 学而思编程ar下载,一起探索吧!
圆的对称性
下图表示一个圆,图中点 ( x , y ) (x,y) (x,y) 位于第1/8象限,利用圆具有的对称性,可以将该点映射到其他的7/8象限上,得到圆周上的其他七个点,即 ( y , x ) (y,x) (y,x)、 ( y , − x ) (y,-x) (y,−x)、 ( x , − y ) (x,-y) (x,−y)、 ( − x . − y ) (-x.-y) (−x.−y)、 ( − y , − x ) (-y,-x) (−y,−x)、 ( − y , x ) (-y,x) (−y,x)、 ( − x , y ) (-x,y) (−x,y)汉语AGI。这样我们只需要小猫计算从 x = 0 x=0 x=0到 x = y x=y x=y这段圆弧就可以得到整个圆的所有像素点的位置了。
需要注意的是,在画圆心在窗口任意位置的圆时,需要将每个要画的点的坐标进行相应的移动,移动的方向和距离是待画圆圆心和坐标系原点的相对位置和距离。
glVertex2f((x0 + x1) * 0.005, (y0 + y1) * 0.005)
glVertex2f((y0 + x1) * 0.005, (x0 + y1) * 0.005)
glVertex2f((x0 + x1) * 0.005, (-y0 + y1) * 0.005)
glVertex2f((y0 + x1) * 0.005, (-x0 + y1) * 0.005)
glVertex2f((-x0 + x1) * 0.005, (y0 + y1) * 0.005)
glVertex2f((-y0 + x1) * 0.005, (x0 + y1) * 0.005)
glVertex2f((-x0 + x1) * 0.005, (-y0 + y1) * 0.005)
glVertex2f((-y0 + x1) * 0.005, (-x0 + y1) * 0.005)
Bresenham画圆法
考虑到圆的对称性,我们只画1/8圆弧。假设当前 P ( x i , y i ) P(x_i,y_i) P(xi,yi) 为当前最佳逼近圆的像素点,Bresenham画圆法的思想是:引入 NE 和 E 的中点 M,如果 M 位于圆内,则 NE 点比较接近圆,所以下一理想的像素点应取 NE 点;反之,如果 M 点在圆外,则 E 点比较接近圆,所以下一理想的像素点应取 E 点。注:图片来源于:黄静.计算机图形学及其实践教程[M].北京:机械工业出版社,2015.5:43
初始值:待画圆的圆心坐标 ( x 1 , y 1 ) (x_1,y_1) (x1,y1),圆的半径 r r r。用 ( x 0 , y 0 ) (x_0,y_0) (x0,y0) 来表示即将要画的点。
将圆的圆心移动到原点上,则圆的直角坐标方程可表示为: x 2 + y 2 = r 2 x^2+y^2=r^2 x2+y2=r2 化为一般式: F ( x , y ) = x 2 + y 2 − r 2 = 0 F(x,y)=x^2+y^2-r^2=0 F(x,y)=x2+y2−r2=0 由数学知识可知:
( 1 ) F ( x , y ) = 0 (1) F(x,y)=0 (1)F(x,y)=0,点在圆上; ( 2 ) F ( x , y ) > 0 (2) F(x,y)>0 (2)F(x,y)>0,点在圆外; ( 1 ) F ( x , y ) < 0 (1) F(x,y)<0 (1)F(x,y)<0,点在圆内。
从圆的上顶点 ( 0 , r ) (0,r) (0,r) 开始从左往右绘制,则初始值为: x 0 = 0 y 0 = r w = F ( M ) = F ( x 0 + 1 , y 0 − 0.5 ) = ( 0 + 1 ) 2 + ( r − 0.5 ) 2 − r 2 = 1.25 − r \begin{align*} x_0&=0\\ y_0&=r\\ w&=F(M)=F(x_0+1,y_0-0.5)\\ &=(0+1)^2+(r-0.5)^2-r^2\\ &=1.25-r \end{align*} x0y0w=0=r=F(M)=F(x0+1,y0−0.5)=(0+1)2+(r−0.5)2−r2=1.25−r 对于任意正在绘制的一点 ( x 0 , y 0 ) (x_0,y_0) (x0,y0),其判别式为: w = F ( M ) = F ( x 0 + 1 , y 0 − 0.5 ) = ( x 0 + 1 ) 2 + ( y 0 − 0.5 ) 2 − r 2 \begin{align*} w&=F(M)=F(x_0+1,y_0-0.5)\\ &=(x_0+1)^2+(y_0-0.5)^2-r^2 \end{align*} w=F(M)=F(x0+1,y0−0.5)=(x0+1)2+(y0−0.5)2−r2 当 w < 0 w<0 w<0时,M 在圆内,此时 NE 点更逼近圆,故下一个点取 NE 点,此时: w = F ( x 0 + 2 , y 0 − 0.5 ) = ( x 0 + 2 ) 2 + ( y 0 − 0.5 ) 2 − r 2 = ( x 0 + 1 ) 2 + ( y 0 − 0.5 ) 2 − r 2 + 2 x 0 + 3 = w + 2 x 0 + 3 x 0 = x 0 + 1 \begin{align*} w&=F(x_0+2,y_0-0.5)\\ &=(x_0+2)^2+(y_0-0.5)^2-r^2\\ &=(x_0+1)^2+(y_0-0.5)^2-r^2+2x_0+3\\ &=w+2x_0+3\\ x_0&=x_0+1 \end{align*} wx0=F(x0+2,y0−0.5)=(x0+2)2+(y0−0.5)2−r2=(x0+1)2+(y0−0.5)2−r2+2x0+3=w+2x0+3=x0+1 当 w ≥ 0 w \geq 0 w≥0时,M 在圆上或圆外,此时视作 E 点更逼近圆,故下一个点取 E 点,此时: w = F ( x 0 + 2 , y 0 − 1.5 ) = ( x 0 + 2 ) 2 + ( y 0 − 1.5 ) 2 − r 2 = ( x 0 + 1 ) 2 + ( y 0 − 0.5 ) 2 − r 2 + 2 x 0 − 2 y 0 + 5 = w + 2 x 0 − 2 y 0 + 5 x 0 = x 0 + 1 y 0 = y 0 − 1 \begin{align*} w&=F(x_0+2,y_0-1.5)\\ &=(x_0+2)^2+(y_0-1.5)^2-r^2\\ &=(x_0+1)^2+(y_0-0.5)^2-r^2+2x_0-2y_0+5\\ &=w+2x_0-2y_0+5\\ x_0&=x_0+1\\ y_0&=y_0-1 \end{align*} wx0y0=F(x0+2,y0−1.5)=(x0+2)2+(y0−1.5)2−r2=(x0+1)2+(y0−0.5)2−r2+2x0−2y0+5=w+2x0−2y0+5=x0+1=y0−1 点 ( x 0 , y 0 ) (x_0,y_0) (x0,y0)和原点构成的直线的斜率为: k = d y 0 d x 0 = y 0 − 0 x 0 − 0 = y 0 x 0 k=\frac{dy_0}{dx_0}=\frac{y_0-0}{x_0-0}=\frac{y_0}{x_0} k=dx0dy0=x0−0y0−0=x0y0 当 k = = 1 k==1 k==1 时,即 y 0 = = x 0 y_0==x_0 y0==x0 时,结束绘制。
除去推理过程可得Bresenham画圆法的完整算法流程:
初始值为: x 0 = 0 y 0 = r w = 1.25 − r \begin{align*} x_0&=0\\ y_0&=r\\ w&=1.25-r \end{align*} x0y0w=0=r=1.25−r 当 w < 0 w<0 w<0时: w = w + 2 x 0 + 3 x 0 = x 0 + 1 \begin{align*} w&=w+2x_0+3\\ x_0&=x_0+1 \end{align*} wx0=w+2x0+3=x0+1 当 w ≥ 0 w \geq 0 w≥0 时: w = w + 2 x 0 − 2 y 0 + 5 x 0 = x 0 + 1 y 0 = y 0 − 1 \begin{align*} w&=w+2x_0-2y_0+5\\ x_0&=x_0+1\\ y_0&=y_0-1 \end{align*} wx0y0=w+2x0−2y0+5=x0+1=y0−1 循环以上过程直到 x 0 = = y 0 x_0==y_0 x0==y0。
以下是Bresenham画圆法的代码实现:
# 中点Bresenham画圆法
def gl_draw_circle(x1, y1, r):
"""
这个函数用于画给定终点和半径的圆
:param x1: 圆心横坐标
:param y1: 圆心纵坐标
:param r: 圆半径
:return: None
"""
x0 = 0
y0 = r
w = 1 - y0 # 是1.25 - y0 取整后的结果
glPointSize(1)
while x0 < y0:
glBegin(GL_POINTS)
glVertex2f((x0 + x1) * 0.005, (y0 + y1) * 0.005)
glVertex2f((y0 + x1) * 0.005, (x0 + y1) * 0.005)
glVertex2f((x0 + x1) * 0.005, (-y0 + y1) * 0.005)
glVertex2f((y0 + x1) * 0.005, (-x0 + y1) * 0.005)
glVertex2f((-x0 + x1) * 0.005, (y0 + y1) * 0.005)
glVertex2f((-y0 + x1) * 0.005, (x0 + y1) * 0.005)
glVertex2f((-x0 + x1) * 0.005, (-y0 + y1) * 0.005)
glVertex2f((-y0 + x1) * 0.005, (-x0 + y1) * 0.005)
glEnd()
x0 += 1
if w < 0:
w = w + 2 * x0 + 3
else:
w = w + 2 * x0 - 2 * y0 + 5
y0 -= 1
x0 += 1
if x0 == y0:
glBegin(GL_POINTS)
glVertex2f((x0 + x1) * 0.005, (y0 + y1) * 0.005)
glVertex2f((y0 + x1) * 0.005, (x0 + y1) * 0.005)
glVertex2f((x0 + x1) * 0.005, (-y0 + y1) * 0.005)
glVertex2f((y0 + x1) * 0.005, (-x0 + y1) * 0.005)
glVertex2f((-x0 + x1) * 0.005, (y0 + y1) * 0.005)
glVertex2f((-y0 + x1) * 0.005, (x0 + y1) * 0.005)
glVertex2f((-x0 + x1) * 0.005, (-y0 + y1) * 0.005)
glVertex2f((-y0 + x1) * 0.005, (-x0 + y1) * 0.005)
glEnd()
绘制圆的完整代码
这里鼠标绘制时第一个点为圆的中点,第二个点为圆上任意一点,通过这两个点来确定圆的圆心和半径,通过拖动鼠标可以改变观察将要绘制的圆的大小。
#!/usr/bin/python
# -*- coding: utf-8 -*-
from import *
# from import *
from import *
import math
mx = 200
my = 200
ex = 200
ey = 200
cur_ex = 200
cur_ey = 200
windowsizex = 400
windowsizey = 400
is_cur_begin_draw = 0 # 判断实时绘画线段是否开始绘制
is_begin_draw = 0 # 判断是否开始绘画
is_end_draw = -1 # 判断是否停止绘画
first_left_button_down = 0
lines_list = [] # 保存已经画好了的线
# 中点Bresenham画圆法
def gl_draw_circle(x1, y1, r):
"""
这个函数用于画给定终点和半径的圆
:param x1: 圆心横坐标
:param y1: 圆心纵坐标
:param r: 圆半径
:return: None
"""
x0 = 0
y0 = r
w = 1 - y0 # 是1.25 - y0 取整后的结果
glPointSize(1)
while x0 < y0:
glBegin(GL_POINTS)
glVertex2f((x0 + x1) * 0.005, (y0 + y1) * 0.005)
glVertex2f((y0 + x1) * 0.005, (x0 + y1) * 0.005)
glVertex2f((x0 + x1) * 0.005, (-y0 + y1) * 0.005)
glVertex2f((y0 + x1) * 0.005, (-x0 + y1) * 0.005)
glVertex2f((-x0 + x1) * 0.005, (y0 + y1) * 0.005)
glVertex2f((-y0 + x1) * 0.005, (x0 + y1) * 0.005)
glVertex2f((-x0 + x1) * 0.005, (-y0 + y1) * 0.005)
glVertex2f((-y0 + x1) * 0.005, (-x0 + y1) * 0.005)
glEnd()
if w < 0:
w = w + 2 * x0 + 3
else:
w = w + 2 * x0 - 2 * y0 + 5
y0 -= 1
x0 += 1
if x0 == y0:
glBegin(GL_POINTS)
glVertex2f((x0 + x1) * 0.005, (y0 + y1) * 0.005)
glVertex2f((y0 + x1) * 0.005, (x0 + y1) * 0.005)
glVertex2f((x0 + x1) * 0.005, (-y0 + y1) * 0.005)
glVertex2f((y0 + x1) * 0.005, (-x0 + y1) * 0.005)
glVertex2f((-x0 + x1) * 0.005, (y0 + y1) * 0.005)
glVertex2f((-y0 + x1) * 0.005, (x0 + y1) * 0.005)
glVertex2f((-x0 + x1) * 0.005, (-y0 + y1) * 0.005)
glVertex2f((-y0 + x1) * 0.005, (-x0 + y1) * 0.005)
glEnd()
def mydisplay():
# print("-----")
glClear(GL_COLOR_BUFFER_BIT)
global is_end_draw
global is_cur_begin_draw
global lines_list
if len(lines_list) != 0:
for item in lines_list:
# 绘制图形
r = int(((item[0] - item[2]) * (item[0] - item[2]) + (item[1] - item[3]) * (item[1] - item[3])))
gl_draw_circle(item[0], item[1], r)
pass
if is_end_draw == 1:
# 将起始点和结束点添加进去
lines_list.append([(mx - int(windowsizex / 2)), -(my - int(windowsizey / 2)),
(ex - int(windowsizex / 2)), -(ey - int(windowsizey / 2))])
glutPostRedisplay()
else:
if is_cur_begin_draw == 1:
# 绘制图形
r = int(((mx - cur_ex) * (mx - cur_ex) + (my - cur_ey) * (my - cur_ey)))
gl_draw_circle((mx - int(windowsizex / 2)), -(my - int(windowsizey / 2)), r)
pass
glutSwapBuffers() # 双缓存的刷新模式
def mymouse(button, state, mousex, mousey):
global mx
global my
if button == GLUT_RIGHT_BUTTON and state == GLUT_DOWN:
print("right_button down")
print("x: ", mousex, " y: ", mousey)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glClearColor(0.0, 0.0, 0.0, 0.0) # 将清空颜色为黑色
if button == GLUT_LEFT_BUTTON:
if state == GLUT_DOWN:
global is_end_draw
global first_left_button_down
global ex
global ey
if first_left_button_down == 0:
mx = mousex
my = mousey
print("left_button down")
print("mx: ", mx, " my: ", my)
is_end_draw = 0
first_left_button_down = 1
glutPostRedisplay() # 重画,相当于重新调用display
elif first_left_button_down == 1:
is_end_draw = 1
first_left_button_down = 0
ex = mousex
ey = mousey
glutPostRedisplay() # 重画,相当于重新调用display
def mymousemotion(x1, y1):
global is_end_draw
global cur_ex, cur_ey
global is_cur_begin_draw
if is_end_draw == 0:
cur_ex = x1
cur_ey = y1
is_cur_begin_draw = 1
glutPostRedisplay() # 重画,相当于重新调用display
else:
is_cur_begin_draw = 0
if __name__ == "__main__":
glutInit() # 对GLUT初始化
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA) # 设置显示方式
glutInitWindowSize(windowsizex, windowsizey)
text = glutCreateWindow("MYTEXT") # 创建窗口,窗口被创建后,需要调用glutMainLoop()才能看到
glutDisplayFunc(mydisplay)
glutMouseFunc(mymouse)
glutPassiveMotionFunc(mymousemotion) # 鼠标按钮松开时移动相应函数
glutMainLoop()
glClearColor(0.0, 0.0, 0.0, 0.0) # 将清空颜色为黑色
glClear(GL_COLOR_BUFFER_BIT) # 将窗口的背景设置为当前颜色
绘制圆的效果展示
绘制椭圆
绘制椭圆的思路与绘制圆的思路大致相同,唯一不同的地方在于绘制椭圆时是1/4对称,我们首先绘制第一象限的椭圆,然后将其对称到其他象限。在绘制第一象限的椭圆弧时,当所绘点处的椭圆切线斜率小于-1时,需要将绘制从以x为步长的绘制改为以y为步长的绘制,以求得到更多的椭圆上的点。
初始值:椭圆的圆心 ( x 1 , y 1 ) (x_1,y_1) (x1,y1),椭圆的横半轴长 a a a,纵半轴长 b b b,用 ( x 0 , y 0 ) (x_0,y_0) (x0,y0) 表示正要绘制的顶点坐标。
将椭圆的圆心移动到原点上,则椭圆的直角坐标方程可表示为: x 2 a 2 + y 2 b 2 = 1 \frac{x^2}{a^2}+\frac{y^2}{b^2}=1 a2x2+b2y2=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+a2y2−a2b2=0 从椭圆的上顶点 ( 0 , b ) (0,b) (0,b) 开始从左往右绘制,则初始值为: x 0 = 0 y 0 = a \begin{align*} x_0&=0\\ y_0&=a\\ \end{align*} x0y0=0=a 对于任意正要绘制的一点 ( x 0 , y 0 ) (x_0,y_0) (x0,y0),其判别式为: w = F ( M ) = F ( x 0 + 1 , y 0 − 0.5 ) = b 2 ( x 0 + 1 ) 2 + a 2 ( y 0 − 0.5 ) 2 − a 2 b 2 \begin{align*} w&=F(M)=F(x_0+1,y_0-0.5)\\ &=b^2(x_0+1)^2+a^2(y_0-0.5)^2-a^2b^2 \end{align*} w=F(M)=F(x0+1,y0−0.5)=b2(x0+1)2+a2(y0−0.5)2−a2b2 为了避免浮点运算,将判别式 w w w 的值乘以4: w = 4 b 2 ( x 0 + 1 ) 2 + a 2 ( 2 y 0 − 1 ) 2 − 4 a 2 b 2 w=4b^2(x_0+1)^2+a^2(2y_0-1)^2-4a^2b^2 w=4b2(x0+1)2+a2(2y0−1)2−4a2b2 当 w < 0 w<0 w<0时,此时: x 0 = x 0 + 1 w = F ( x 0 + 2 , y 0 − 0.5 ) = 4 b 2 ( x 0 + 2 ) 2 + a 2 ( 2 y 0 − 1 ) 2 − 4 a 2 b 2 = w + 8 b 2 x 0 + 12 b 2 = w + 4 b 2 ( 2 x 0 + 3 ) \begin{align*} x_0&=x_0+1\\ w&=F(x_0+2,y_0-0.5)\\ &=4b^2(x_0+2)^2+a^2(2y_0-1)^2-4a^2b^2\\ &=w+8b^2x_0+12b^2\\ &=w+4b^2(2x_0+3) \end{align*} x0w=x0+1=F(x0+2,y0−0.5)=4b2(x0+2)2+a2(2y0−1)2−4a2b2=w+8b2x0+12b2=w+4b2(2x0+3) 当 w ≥ 0 w \geq 0 w≥0时,此时: x 0 = x 0 + 1 y 0 = y 0 − 1 w = F ( x 0 + 2 , y 0 − 1.5 ) = 4 b 2 ( x 0 + 2 ) 2 + a 2 ( 2 y 0 − 3 ) 2 − 4 a 2 b 2 = w + 4 b 2 ( 2 x 0 + 3 ) + 4 a 2 ( 2 − 2 y 0 ) \begin{align*} x_0&=x_0+1\\ y_0&=y_0-1\\ w&=F(x_0+2,y_0-1.5)\\ &=4b^2(x_0+2)^2+a^2(2y_0-3)^2-4a^2b^2\\ &=w+4b^2(2x_0+3)+4a^2(2-2y_0) \end{align*} x0y0w=x0+1=y0−1=F(x0+2,y0−1.5)=4b2(x0+2)2+a2(2y0−3)2−4a2b2=w+4b2(2x0+3)+4a2(2−2y0) 椭圆的方程为: 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+a2y2−a2b2=0 两边同时对x求导: 2 b 2 x + 2 a 2 y d y d x = 0 2b^2x+2a^2y\frac{dy}{dx}=0 2b2x+2a2ydxdy=0 故: d y d x = − b 2 x a 2 y \frac{dy}{dx}=-\frac{b^2x}{a^2y} dxdy=−a2yb2x 当 d y d x > − 1 \frac{dy}{dx}>-1 dxdy>−1 时,即 b 2 x − a 2 y < 0 b^2x-a^2y<0 b2x−a2y<0 时,以 x x x 为步长绘制椭圆。当 b 2 x − a 2 y > 0 b^2x-a^2y>0 b2x−a2y>0 时,开始转化为以 y y y 为步长绘制椭圆,当 y 0 = = 0 y_0==0 y0==0 时结束绘制。以 y y y 为步长的椭圆绘制算法的推演与以 x x x 为步长绘制椭圆的算法推演类似。
下面时绘制椭圆的代码实现:
# 椭圆绘制算法
def gl_draw_ellipse(x1, y1, m_a, m_b):
"""
这是用来绘制椭圆的函数
:param x1: 椭圆中心横坐标
:param y1: 椭圆中心纵坐标
:param m_a: 椭圆横轴长
:param m_b: 椭圆纵轴长
:return: None
"""
x0 = 0
y0 = m_b
m_a2 = m_a * m_a
m_b2 = m_b * m_b
w = 4 * m_b2 * (x0 + 1) * (x0 + 1) + \
m_a2 * (2 * y0 - 1) * (2 * y0 - 1) - \
4 * m_a2 * m_b2 # 判别式,用来判断下一个点的选取
glPointSize(1)
# 判断当前点所在椭圆的切线的斜率,如果斜率小于-1,则不交换
while m_b2 * x0 - m_a2 * y0 < 0:
glBegin(GL_POINTS)
glVertex2f((x0 + x1) * 0.005, (y0 + y1) * 0.005)
glVertex2f((x0 + x1) * 0.005, (-y0 + y1) * 0.005)
glVertex2f((-x0 + x1) * 0.005, (y0 + y1) * 0.005)
glVertex2f((-x0 + x1) * 0.005, (-y0 + y1) * 0.005)
glEnd()
x0 += 1
if w < 0:
w = w + 4 * m_b2 * (2 * x0 + 3)
else:
y0 -= 1
w = w + 4 * m_b2 * (2 * x0 + 3) + 4 * m_a2 * (2 - 2 * y0)
if m_b2 * x0 - m_a2 * y0 == 0:
glBegin(GL_POINTS)
glVertex2f((x0 + x1) * 0.005, (y0 + y1) * 0.005)
glVertex2f((x0 + x1) * 0.005, (-y0 + y1) * 0.005)
glVertex2f((-x0 + x1) * 0.005, (y0 + y1) * 0.005)
glVertex2f((-x0 + x1) * 0.005, (-y0 + y1) * 0.005)
glEnd()
w = m_b2 * (2 * x0 + 1) * (2 * x0 + 1) + \
4 * m_a2 * (y0 - 1) * (y0 - 1) - \
4 * m_a2 * m_b2
while x0 < m_a and y0 >= 0:
glBegin(GL_POINTS)
glVertex2f((x0 + x1) * 0.005, (y0 + y1) * 0.005)
glVertex2f((x0 + x1) * 0.005, (-y0 + y1) * 0.005)
glVertex2f((-x0 + x1) * 0.005, (y0 + y1) * 0.005)
glVertex2f((-x0 + x1) * 0.005, (-y0 + y1) * 0.005)
glEnd()
y0 -= 1
if w < 0:
x0 += 1
w = w + 4 * m_b2 * (2 * x0 + 2) + 4 * m_a2 * (-2 * y0 + 3)
else:
w = w + 4 * m_a2 * (-2 * y0 + 3)
绘制椭圆的完整代码
这里鼠标点击的第一个点是椭圆外切矩形的其中一个顶点,第二个点为和第一个点互为对角的椭圆外切矩形的另一个顶点,从这两个顶点推出椭圆圆心、长半轴长和短半轴长,通过移动鼠标可以观察将要绘制的椭圆的实时变化,以下是绘制椭圆的完整代码实现。
#!/usr/bin/python
# -*- coding: utf-8 -*-
from import *
# from import *
from import *
# import math
mx = 200
my = 200
ex = 200
ey = 200
cur_ex = 200
cur_ey = 200
windowsizex = 400
windowsizey = 400
is_cur_begin_draw = 0 # 判断实时绘画线段是否开始绘制
is_begin_draw = 0 # 判断是否开始绘画
is_end_draw = -1 # 判断是否停止绘画
first_left_button_down = 0
lines_list = [] # 保存已经画好了的线
# 椭圆绘制算法
def gl_draw_ellipse(x1, y1, m_a, m_b):
"""
这是用来绘制椭圆的函数
:param x1: 椭圆中心横坐标
:param y1: 椭圆中心纵坐标
:param m_a: 椭圆横轴长
:param m_b: 椭圆纵轴长
:return: None
"""
x0 = 0
y0 = m_b
m_a2 = m_a * m_a
m_b2 = m_b * m_b
w = 4 * m_b2 * (x0 + 1) * (x0 + 1) + \
m_a2 * (2 * y0 - 1) * (2 * y0 - 1) - \
4 * m_a2 * m_b2 # 判别式,用来判断下一个点的选取
glPointSize(1)
# 判断当前点所在椭圆的切线的斜率,如果斜率小于-1,则不交换
while m_b2 * x0 - m_a2 * y0 < 0:
glBegin(GL_POINTS)
glVertex2f((x0 + x1) * 0.005, (y0 + y1) * 0.005)
glVertex2f((x0 + x1) * 0.005, (-y0 + y1) * 0.005)
glVertex2f((-x0 + x1) * 0.005, (y0 + y1) * 0.005)
glVertex2f((-x0 + x1) * 0.005, (-y0 + y1) * 0.005)
glEnd()
x0 += 1
if w < 0:
w = w + 4 * m_b2 * (2 * x0 + 3)
else:
y0 -= 1
w = w + 4 * m_b2 * (2 * x0 + 3) + 4 * m_a2 * (2 - 2 * y0)
if m_b2 * x0 - m_a2 * y0 == 0:
glBegin(GL_POINTS)
glVertex2f((x0 + x1) * 0.005, (y0 + y1) * 0.005)
glVertex2f((x0 + x1) * 0.005, (-y0 + y1) * 0.005)
glVertex2f((-x0 + x1) * 0.005, (y0 + y1) * 0.005)
glVertex2f((-x0 + x1) * 0.005, (-y0 + y1) * 0.005)
glEnd()
w = m_b2 * (2 * x0 + 1) * (2 * x0 + 1) + \
4 * m_a2 * (y0 - 1) * (y0 - 1) - \
4 * m_a2 * m_b2
while x0 < m_a and y0 >= 0:
glBegin(GL_POINTS)
glVertex2f((x0 + x1) * 0.005, (y0 + y1) * 0.005)
glVertex2f((x0 + x1) * 0.005, (-y0 + y1) * 0.005)
glVertex2f((-x0 + x1) * 0.005, (y0 + y1) * 0.005)
glVertex2f((-x0 + x1) * 0.005, (-y0 + y1) * 0.005)
glEnd()
y0 -= 1
if w < 0:
x0 += 1
w = w + 4 * m_b2 * (2 * x0 + 2) + 4 * m_a2 * (-2 * y0 + 3)
else:
w = w + 4 * m_a2 * (-2 * y0 + 3)
def mydisplay():
# print("-----")
# global windowsizex, windowsizey
# windowsizex = glutGet(GLUT_WINDOW_X)
# windowsizey = glutGet(GLUT_WINDOW_Y)
glClear(GL_COLOR_BUFFER_BIT)
global is_end_draw
global is_cur_begin_draw
global lines_list
if len(lines_list) != 0:
for item in lines_list:
# 绘制图形
m_a = int(abs(item[0] - item[2]) / 2)
m_b = int(abs(item[1] - item[3]) / 2)
mid_x = (item[0] + item[2]) / 2
mid_y = (item[1] + item[3]) / 2
gl_draw_ellipse(mid_x, mid_y, m_a, m_b)
pass
if is_end_draw == 1:
# 将起始点和结束点添加进去
lines_list.append([(mx - int(windowsizex / 2)), -(my - int(windowsizey / 2)),
(ex - int(windowsizex / 2)), -(ey - int(windowsizey / 2))])
glutPostRedisplay()
else:
if is_cur_begin_draw == 1:
# 绘制图形
m_a = int(abs(mx - cur_ex)/2)
m_b = int(abs(my - cur_ey)/2)
mid_x = (mx + cur_ex)/2
mid_y = (my + cur_ey)/2
gl_draw_ellipse((mid_x - int(windowsizex / 2)), -(mid_y - int(windowsizey / 2)), m_a, m_b)
pass
glutSwapBuffers() # 双缓存的刷新模式
def mymouse(button, state, mousex, mousey):
global mx
global my
if button == GLUT_RIGHT_BUTTON and state == GLUT_DOWN:
print("right_button down")
print("x: ", mousex, " y: ", mousey)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glClearColor(0.0, 0.0, 0.0, 0.0) # 将清空颜色为黑色
if button == GLUT_LEFT_BUTTON:
if state == GLUT_DOWN:
global is_end_draw
global first_left_button_down
global ex
global ey
if first_left_button_down == 0:
mx = mousex
my = mousey
print("left_button down")
print("mx: ", mx, " my: ", my)
is_end_draw = 0
first_left_button_down = 1
glutPostRedisplay() # 重画,相当于重新调用display
elif first_left_button_down == 1:
is_end_draw = 1
first_left_button_down = 0
ex = mousex
ey = mousey
glutPostRedisplay() # 重画,相当于重新调用display
def mymousemotion(x1, y1):
global is_end_draw
global cur_ex, cur_ey
global is_cur_begin_draw
if is_end_draw == 0:
cur_ex = x1
cur_ey = y1
is_cur_begin_draw = 1
glutPostRedisplay() # 重画,相当于重新调用display
else:
is_cur_begin_draw = 0
if __name__ == "__main__":
glutInit() # 对GLUT初始化
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA) # 设置显示方式
glutInitWindowSize(windowsizex, windowsizey)
text = glutCreateWindow("MYTEXT") # 创建窗口,窗口被创建后,需要调用glutMainLoop()才能看到
glutDisplayFunc(mydisplay)
glutMouseFunc(mymouse)
glutPassiveMotionFunc(mymousemotion) # 鼠标按钮松开时移动相应函数
glutMainLoop()
glClearColor(0.0, 0.0, 0.0, 0.0) # 将清空颜色为黑色
glClear(GL_COLOR_BUFFER_BIT) # 将窗口的背景设置为当前颜色
绘制椭圆的效果展示
结语
本文算法推演部分部分来自于参考文献的摘要,实现的代码未经优化,仅供学习与参考使用,如有错误,敬请谅解。
参考文献
[1] 黄静.计算机图形学及其实践教程[M].北京:机械工业出版社,2015.5:41-44 [2] 徐文鹏.计算机图形学基础(OpenGL版)[M].北京:清华大学出版社,2016.6:50-55
原文地址1:https://blog.csdn.net/m0_72786913/article/details/136128815
参考资料:python中用turtle画一个圆形 https://blog.csdn.net/SXIAOYAN_/article/details/140061099