中点分割裁剪算法(python)
实验目的
采用中点分割方法找到距离线段顶点最近的可见点,找到后,进行绘制,即可实现直线段在裁剪窗口的裁剪显示。
算法思想
设要裁剪的线段是P1P2。中点分割算法可分成两个平行的过程进行,即从P1点出发找出离P1最远的可见点(图中的B点),和从P2点出发找出离P2最远的可见点(图中的A点)。这两个可见点的连线就是原线段的可见部分。
算法步骤
(1)以P1出发,检测点P2是否在窗口内,若是,即是所求点;若不转(2)
(2)检测点P1P2是否在同一外侧,若是,则不在窗口内;返回;不转(3)
(3)求出该直线段的中点P3。检测P3与P1P2的关系:
若中点P3在窗口内,用P3代替P1;重复步骤(3);
若中点P3不在窗口内,判断P3与P2的关系,
若P3、P2在同侧外面,则用P3代替P2,即丢掉P2P3段;
若不在同侧外面,则用P3代替P1,重复步骤(3)。直到满足误差条件,则该中点就是该线段落距离P1最远的可见点。
(4)重复步骤(2)-(3),求出另一个端点P2的最远可见点坐标。
测试代码
采用多边形来进行裁剪,只是对多边形的每条边单独裁剪画线。等同于是直线的集合,在这里我选择的测试多边形,边有全在窗口内的,有全部在窗口内的,有部分在窗口内部分在窗口外,还有更特殊的:顶点
在裁剪窗口上,中点在裁剪窗口上等等,这样更具有良好的测试作用 polygon=[
[20,20],
[120,20],
[70,100],
[50,80],
[30,120],
[20,50],
[50,50]
]
]
实验结果与分析
空间复杂度:空间复杂度可忽略不计
时间复杂度:单*条线段时,假设迭代次数为c,时间复杂度近似为O(2*c)。
裁剪多边形每一条边时,时间复杂度近似为O(2*c*n)
算法亮点与缺陷和感想
亮点:中点分割算法相比于Cohen-Sutherland算法,在一开始都是通过编码的思想去除了大量的直线全在裁剪窗口内和全在裁剪窗口外避免了求交,只需计算中点坐标即可完成,宜于硬件实现。
缺陷:但是需要大量循环,而且我在编写这个代码的时候,虽然中点算法思路比较简单,不需要考虑大量的运算,但是它却是比较片面或者说是理想化的,就只是考虑了一些普遍的情况,像是比如中点在裁剪窗口边界上,斜率为0的情况等特殊情况没有加以考虑进去,只能单独考虑这些情况,设置多个条件判断来进行剔除、考虑。
感想:中点分割算法采用中点法来解决线段裁剪问题,这个中点的思想运用很广泛,在最开始接触直线的画法时,就接触到了中点方法。中点法在对于处理一条具体的直线上运用的比较多,但对于后面的图形就没有中点这个概念了。所以对于处理一条特定的线段,中点法是可以被考虑的。在编程思想中,还有二分查找也是取中点进行处理的。可见中点思想是一个很普遍的思想,对于一系列相同的离散或连续的元素,对它们加以操作时,可以考虑中点法。另外此算法的可行性在于屏幕像素是有限的而且我们人为的对它进行了设置最小长度,一般计算次数不会太多,算法的思想是用取中点分方法最后在误差允许的范围内取逼近真正的与裁剪窗口的交点。
代码:
'''
中点分割裁剪算法
'''
import matplotlib.pyplot as plt
import numpy as np
import cmath
LEFT=1 #0001 左
RIGHT=2 #0010 右
BOTTOM=4 #0100 下
TOP=8 #1000 上
XL=40
XR=100
YB=40
YT=100
def DDALine(image,x0, y0, x1, y1, color):
if dx != 0:
k = dy / dx
else:
for i in range(y0, y0 + dy + 1):
image[i][x0] = color
else:
for i in range(y1, y1 - dy + 1):
image[i][x0] = color
return
if k>=0:
if abs(k)<1:
y = y0
xmin = x0
xmax = x1
for i in range(xmin,xmax+1):
image[int(y+0.5)][i] = color
y = y+k
else:
x = x0
if y0<y1:
ymin = y0
ymax = y1
else:
ymin = y1
ymax = y0
for i in range(ymin,ymax+1):
image[i][int(x+0.5)] = color
x = x + 1.0/k
if k<0 : #k<0
if k>-1:
y = y0
xmin = x0
xmax = x1
for i in range(xmin,xmax+1):
image[int(y+0.5)][i] = color
y = y+k
else:
x = x1
if y0<y1:
ymin = y0
ymax = y1
else:
ymin = y1
ymax = y0
for i in range(ymin,ymax+1):
image[i][int(x+0.5)] = color
x=x+1.0/k
def draw(image,graph,color):
l = len(graph)
for i in range(l):
[x0, y0] = graph[i]
[x1, y1] = graph[(i + 1 + l) % l]
if x0 > x1:
temp = x1
x1 = x0
x0 = temp
temp = y1
y1 = y0
y0 = temp
DDALine(image, x0, y0, x1, y1, color)
def encode(x,y):
c=0
if(x<XL):
c=c|LEFT
if(x>XR):
c= c|RIGHT
if(y<YB):
c= c|BOTTOM
if(y>YT):
c=c|TOP
return c
def MidClip(polygon,image):
image=image
l = len(polygon)
for i in range(l):
[x1, y1] = polygon[i]
[x2, y2] = polygon[(i + 1 + l) % l]
print(x1)
print(y1)
print(x2)
print(y2)
print("\n")
flag=0
code1=encode(x1,y1) #p1
code2=encode(x2,y2) #p2
print(code1)
print(code2)
if(code1==0 and code2==0): #同时为0,在窗口内部,不用裁剪
flag=2
elif(code1 & code2 !=0): #在窗口外部
flag=1
else:
ff=0
#s=2 #避免计算
if (code2 == 0):
[xa, ya] = [x2, y2] # 检测p2是否在窗口内部,是则为所求点
ff=1
while((abs(x1-x2)>1 or abs(y1-y2)>1) and ff==0): #从p1出发
#xmid=(int)((x1+x2)/2)
#ymid=(int)((y1+y2)/2)
print(x1)
print(x2)
xmid = (x1 + x2) / 2
ymid=(y1+y2)/2
codem=encode(xmid,ymid)
print(xmid)
print(ymid)
print(codem)
if(((ymid==YT)and(XL<=xmid<=XR)) or ((ymid==YB)and(XL<=xmid<=XR))):
[xa,ya]=[xmid,ymid]
break
elif(codem==0): #若中点在窗口内部
x1=xmid
y1=ymid
elif(codem & code2 !=0): #中点和p2在同一侧外面
x2=xmid
y2=ymid
elif(codem & code1 !=0):
x1=xmid
y1=ymid
#s=(float)(cmath.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)))
#s=2
#exit(0)
if (abs(x1 - x2) <=1 and abs(y1 - y2) <=1):
[xa, ya] = [x2, y2]
[x1, y1] = polygon[i]
[x2, y2] = polygon[(i + 1 + l) % l]
fff=0
if (code1 == 0):
[xb, yb] = [x1, y1] # 检测p1是否在窗口内部,是则为所求点
fff=1
while ((abs(x1 - x2) > 1 or abs(y1 - y2) > 1 ) and fff==0): # 从p2出发
#xmid = (int)((x1 + x2) / 2)
#ymid = (int)((y1 + y2) / 2)
xmid = (x1 + x2) / 2
ymid = (y1 + y2) / 2
codem = encode(xmid, ymid)
if (((ymid == YT) and (XL <= xmid <= XR)) or ((ymid == YB) and (XL <= xmid <= XR))):
[xa, ya] = [xmid, ymid]
break
elif (codem == 0): # 若中点在窗口内部
x2 = xmid
y2 = ymid
elif (codem & code1 !=0): # 中点和p2在同一侧外面
x1 = xmid
y1 = ymid
elif (codem & code2 !=0):
x2 = xmid
y2 = ymid
#s = (float)(cmath.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)))
if (abs(x1 - x2) <=1 and abs(y1 - y2) <=1):
[xb, yb] = [x1, y1]
if (flag == 1):
pass
elif(flag==2):
if (x1 > x2):
temp = x2
x2 = x1
x1 = temp
temp = y2
y2 = y1
y1 = temp
DDALine(image, x1, y1,x2, y2, False)
else:
if(xa > xb):
temp = xb
xb = xa
xa = temp
temp = yb
yb = ya
ya = temp
DDALine(image, (int)(xa+0.5), (int)(ya+0.5), (int)(xb+0.5), (int)(yb+0.5), False)
if __name__ == '__main__':
image = np.ones([150, 150])
plt.xlim(0, 150)
plt.ylim(0, 150)
window=[
[40,40],
[40,100],
[100,100],
[100,40]
]
polygon = [
[20, 20],
[120, 20],
[70, 100],
[50, 80],
[30, 120],
[20, 50],
[50, 50]
]
draw(image,window,False)
#draw(image,polygon,False)
MidClip(polygon,image)
plt.imshow(image, plt.cm.gray)
plt.show()