plt.fill无法覆盖已有绘图及多图保留前置绘图问题求助
解决Matplotlib绘图遮挡与迭代图像生成问题
问题描述
我需要迭代生成多张图像,仅保留30度视角范围内的内容,其余区域用遮挡层覆盖。当前代码存在两个问题:
- plt.fill绘制的遮挡层无法覆盖图中的蓝色和黑色线条
- 前置绘制的蓝色线条未出现在所有输出图像中
原代码
import matplotlib.pyplot as plt import numpy as np # number of interior walls intwallnum = 3 # room x= np.linspace(1, 22, 100) y = np.linspace(1, 20, 100) #plt.plot(x, y, 'w') #interior walls case 1 x1 = 8 x2 = 13 y1 = 5 y2 = 10 y3 = 18 numpoints = 50 w1 = np.linspace(x1, x2, numpoints) xlist = [x1, x2] ylist = [y1, y2, y3] y1list = np.zeros(numpoints) y2list = np.zeros(numpoints) y3list = np.zeros(numpoints) for i in np.arange(numpoints): y1list[i] = y1 y2list[i] = y2 y3list[i] = y3 plt.plot(w1, y1list, 'k', linewidth = 3) plt.plot(w1, y2list, 'k', linewidth = 3) plt.plot(w1, y3list, 'k', linewidth = 3) #lines of view cam1 = [0, 20] cam2 = [22, 0] # adjusted to apply with this prob specifically def eq(xpoint, ypoint, cam): m = (cam[1] - ypoint)/(cam[0] - xpoint) b = cam[1] - m*cam[0] y = m*np.linspace(0,22,22) + b return y def eq2(xpoint, ypoint, cam): m = (cam[1] - ypoint)/(cam[0] - xpoint) b = cam[1] - m*cam[0] y = m*np.linspace(0,22,22) + b return [m, b] fill_listx = [] fill_listy = [] fill_listx1 = [] fill_listy1 = [] #https://www.geeksforgeeks.org/remove-elements-larger-than-a-specific-value-from-a-list-in-python/ for a in np.arange(len(xlist)*len(ylist)): for j in np.arange(len(ylist)): for i in np.arange(len(xlist)): #plt.plot(np.linspace(0, 22, 22), eq(xlist[i], ylist[j], cam1), 'r', linewidth=1.) plt.plot(np.linspace(0, 22, 22), eq(xlist[i], ylist[j], cam2), 'b', linewidth=1.) if eq2(xlist[i], ylist[j], cam2)[1] >= 20: ptx = (20 - eq2(xlist[i], ylist[j], cam2)[1])/(eq2(xlist[i], ylist[j], cam2)[0]) else: ptx = 0 if eq2(xlist[i], ylist[j], cam2)[1] >= 20: pty = 20 else: pty = eq2(xlist[i], ylist[j], cam2)[1] fill_listx = np.append(fill_listx, ptx) fill_listy = np.append(fill_listy, pty) #camera 1 if eq2(xlist[i], ylist[j], cam1)[0]*(22)+eq2(xlist[i], ylist[j], cam1)[1] < 0: ptx = ( - eq2(xlist[i], ylist[j], cam1)[1])/(eq2(xlist[i], ylist[j], cam1)[0]) else: ptx = 22 if eq2(xlist[i], ylist[j], cam1)[0]*(22)+eq2(xlist[i], ylist[j], cam1)[1] >= 0: pty = (eq2(xlist[i], ylist[j], cam1)[0]*22+eq2(xlist[i], ylist[j], cam1)[1]) else: pty = 0 fill_listx1 = np.append(fill_listx1, ptx) fill_listy1 = np.append(fill_listy1, pty) for i in np.arange(intwallnum): y = ylist[i] if fill_listy[2*i+1] == fill_listx[2*i] or fill_listx[2*i] == fill_listx[2*i+1]: filly = [fill_listy[2*i], fill_listy[2*i+1], ylist[i], ylist[i]] fillx = [fill_listx[2*i], fill_listx[2*i+1], xlist[1], xlist[0]] else: filly = [fill_listy[2*i],20, fill_listy[2*i+1], ylist[i], ylist[i]] fillx = [fill_listx[2*i],0, fill_listx[2*i+1], xlist[1], xlist[0]] plt.fill(fillx, filly, facecolor='w', alpha=1) #camera 1 for i in np.arange(intwallnum): y = ylist[i] if fill_listy1[2*i+1] == fill_listx1[2*i] or fill_listx1[2*i] == fill_listx1[2*i+1]: filly1 = [fill_listy1[2*i], fill_listy1[2*i+1], ylist[i], ylist[i]] fillx1 = [fill_listx1[2*i], fill_listx1[2*i+1], xlist[1], xlist[0]] else: filly1 = [fill_listy1[2*i],0, fill_listy1[2*i+1], ylist[i], ylist[i]] fillx1 = [fill_listx1[2*i],22, fill_listx1[2*i+1], xlist[1], xlist[0]] plt.fill(fillx1, filly1, facecolor='g', alpha=.75) theta_deg = 15 for i in np.arange(1, 90/theta_deg): angle = i*theta_deg x30 = np.linspace(0, 22, 22) y30 = -np.tan(angle*np.pi/180)*x30 + np.tan(angle*np.pi/180)*22 y2 = -np.tan((angle+30)*np.pi/180)*x30 + np.tan((angle+30)*np.pi/180)*22 if np.tan(angle*np.pi/180)*22 > 20: #y-intercept fillx = [22, 0, 0, (20 - np.tan(angle*np.pi/180)*22)/-np.tan(angle*np.pi/180)] filly = [0, 0, 22, 20] plt.fill(fillx, filly, facecolor='y', alpha=1) plt.plot(x30, y30, 'g','--') else: # Too lazy to code exception: line intesects (0, 20) fillx = [0, 22, 22, 0] filly = [20, 20, 0, np.tan(angle*np.pi/180)*22] plt.fill(fillx, filly, facecolor='y', alpha=1) plt.plot(x30, y30, 'g','--') #ax.set_facecolor("k") plt.xlim(0,22) plt.ylim(0,20) plt.figure(figsize=(20, 22)) plt.show()
问题原因分析
- 遮挡层无法覆盖线条:Matplotlib绘图元素按代码执行顺序叠加,先绘制的元素在底层,后绘制的在顶层。原代码先画黑色墙壁和蓝色视线,再绘制遮挡层,导致遮挡层在底层,无法覆盖上方的线条。
- 蓝色线条缺失:原代码中蓝色视线和墙壁仅在循环外绘制一次,后续迭代生成新图像时未重新绘制这些元素,因此只有第一张图有蓝色线条,后续图缺失。
修改后的代码
import matplotlib.pyplot as plt import numpy as np # 室内墙壁数量 intwallnum = 3 # 房间边界参数 x1_wall = 8 x2_wall = 13 y_walls = [5, 10, 18] numpoints = 50 w1 = np.linspace(x1_wall, x2_wall, numpoints) # 视线相机位置 cam1 = [0, 20] cam2 = [22, 0] # 计算视线方程 def eq(xpoint, ypoint, cam): m = (cam[1] - ypoint)/(cam[0] - xpoint) b = cam[1] - m*cam[0] x_vals = np.linspace(0,22,22) y_vals = m * x_vals + b return x_vals, y_vals def eq2(xpoint, ypoint, cam): m = (cam[1] - ypoint)/(cam[0] - xpoint) b = cam[1] - m*cam[0] return [m, b] # 预计算遮挡区域的点(无需每次循环重复计算) fill_listx = [] fill_listy = [] fill_listx1 = [] fill_listy1 = [] xlist = [x1_wall, x2_wall] ylist = y_walls for j in np.arange(len(ylist)): for i in np.arange(len(xlist)): # 计算cam2的遮挡点 m, b = eq2(xlist[i], ylist[j], cam2) if b >= 20: ptx = (20 - b)/m pty = 20 else: ptx = 0 pty = b fill_listx.append(ptx) fill_listy.append(pty) # 计算cam1的遮挡点 m1, b1 = eq2(xlist[i], ylist[j], cam1) if m1*22 + b1 < 0: ptx1 = (-b1)/m1 pty1 = 0 else: ptx1 = 22 pty1 = m1*22 + b1 fill_listx1.append(ptx1) fill_listy1.append(pty1) # 迭代生成每张视角图像 theta_deg = 15 for i in np.arange(1, int(90/theta_deg)): # 1. 新建画布 plt.figure(figsize=(20, 22)) # 2. 绘制基础元素:墙壁、蓝色视线 # 绘制黑色墙壁 for y in y_walls: y_list = np.full(numpoints, y) plt.plot(w1, y_list, 'k', linewidth=3) # 绘制蓝色视线 for j in np.arange(len(ylist)): for i_x in np.arange(len(xlist)): x_vals, y_vals = eq(xlist[i_x], ylist[j], cam2) plt.plot(x_vals, y_vals, 'b', linewidth=1.) # 3. 绘制原有遮挡层(白色和绿色) # cam2的白色遮挡 for idx in np.arange(intwallnum): y = ylist[idx] if fill_listy[2*idx+1] == fill_listx[2*idx] or fill_listx[2*idx] == fill_listx[2*idx+1]: filly = [fill_listy[2*idx], fill_listy[2*idx+1], y, y] fillx = [fill_listx[2*idx], fill_listx[2*idx+1], xlist[1], xlist[0]] else: filly = [fill_listy[2*idx],20, fill_listy[2*idx+1], y, y] fillx = [fill_listx[2*idx],0, fill_listx[2*idx+1], xlist[1], xlist[0]] plt.fill(fillx, filly, facecolor='w', alpha=1) # cam1的绿色遮挡 for idx in np.arange(intwallnum): y = ylist[idx] if fill_listy1[2*idx+1] == fill_listx1[2*idx] or fill_listx1[2*idx] == fill_listx1[2*idx+1]: filly1 = [fill_listy1[2*idx], fill_listy1[2*idx+1], y, y] fillx1 = [fill_listx1[2*idx], fill_listx1[2*idx+1], xlist[1], xlist[0]] else: filly1 = [fill_listy1[2*idx],0, fill_listy1[2*idx+1], y, y] fillx1 = [fill_listx1[2*idx],22, fill_listx1[2*idx+1], xlist[1], xlist[0]] plt.fill(fillx1, filly1, facecolor='g', alpha=.75) # 4. 计算当前30度视角的边界,绘制黄色遮挡层(覆盖视角外区域) angle = i * theta_deg x30 = np.linspace(0, 22, 22) y30 = -np.tan(angle*np.pi/180)*x30 + np.tan(angle*np.pi/180)*22 y30_upper = -np.tan((angle+30)*np.pi/180)*x30 + np.tan((angle+30)*np.pi/180)*22 # 绘制视角边界线 plt.plot(x30, y30, 'g', linestyle='--') plt.plot(x30, y30_upper, 'g', linestyle='--') # 绘制视角外的黄色遮挡层 plt.fill_between(x30, 0, y30, facecolor='y', alpha=1) plt.fill_between(x30, y30_upper, 20, facecolor='y', alpha=1) # 设置坐标轴范围 plt.xlim(0,22) plt.ylim(0,20) # 显示当前图像 plt.show()
关键改动说明
- 画布与元素绘制顺序:将新建画布的操作放到迭代循环最开始,每次迭代都创建新图;同时把墙壁、蓝色视线的绘制逻辑移到循环内部,确保每张图都包含这些基础元素。
- 遮挡层层级调整:所有基础元素绘制完成后,再绘制各类遮挡层(白色、绿色、黄色),保证遮挡层在元素上方,能够覆盖不需要显示的内容。
- 优化视角遮挡逻辑:改用
fill_between更简洁地实现视角外区域的遮挡,避免原代码中手动构造多边形的复杂逻辑,同时确保遮挡区域完整覆盖视角外的线条。
内容的提问来源于stack exchange,提问作者user20678942
相关产品推荐
相关产品推荐

