網(wǎng)上有很多關于pos機器動圖,基于python win32setpixel api 實現(xiàn)計算機圖形學相關操作的知識,也有很多人為大家解答關于pos機器動圖的問題,今天pos機之家(www.rcqwhg.com)為大家整理了關于這方面的知識,讓我們一起來看下吧!
本文目錄一覽:
1、pos機器動圖
pos機器動圖
最近讀研期間上了計算機可視化的課,老師也對計算機圖形學的實現(xiàn)布置了相關的作業(yè)。雖然我沒有系統(tǒng)地學過圖形可視化的課,但是我之前逆向過一些游戲引擎,除了保護驅動之外,因為要做透視,接觸過一些計算機圖形學的基礎常識。這次的作業(yè)主要分為2個主要模塊,一個是實現(xiàn)畫線,畫圓的算法,還有填充的算法,以及裁剪的算法。之前工作的時候雖然參與過一些數(shù)據(jù)可視化大屏的設計,但是當時主要的工作使用Echarts或者G2做業(yè)務組件開發(fā),并沒有對畫線,填充,裁剪等基礎算法做過實現(xiàn)。這次就著這個機會我就想了解一些。實現(xiàn)的效果如下(動圖加載可能有些慢):
掃描線填充過程#裁剪過程(根據(jù)鼠標位置,實時裁剪多邊形,右邊的藍色是裁剪后的圖形)#為什么選擇win32 api畫圖#選擇win32的原因是我想做一些與眾不同的實現(xiàn)方法,比起用D3或者Echarts這種webGL的實現(xiàn)方式,我更想直接在顯示器上畫出圖像,看起來更極客一些。這也導致了錄屏軟件沒辦法捕捉,只能用手機來錄制
為什么不選C++而選擇python#主要是python能對內存做個管理,C++直接調這種底層的接口會把內存搞壞掉,導致電腦變得特別卡。。不信大家可以在電腦上編譯運行這段代碼2分鐘試一試,如果你電腦沒炸,算你有錢。。
Copy#include <windows.h>// g++ a.cpp -o a.exe -lgdi32 && a.exevoid bresenham(int x0,int y0,int x1,int y1){ int dx = abs(x1-x0); int dy = abs(y1-y0); int sx = x0<x1 ? 1 : -1; int sy = y0<y1 ? 1 : -1; int err = dx-dy; int e2; while(1){ SetPixel(GetDC(0),x0,y0,RGB(255,0,0)); if(x0==x1 && y0==y1) break; e2 = 2*err; if(e2>-dy){ err = err-dy; x0 = x0+sx; } if(e2<dx){ err = err+dx; y0 = y0+sy; } }}void draw_polygon(int x[], int y[], int n){ int i; for (i = 0; i < n - 1; i++) bresenham(x[i], y[i], x[i + 1], y[i + 1]); bresenham(x[n - 1], y[n - 1], x[0], y[0]);}int main(){ HDC hdc = GetDC(0); int x[4] = {100,200,300,100}; int y[4] = {100,100,200,200}; while (1) { draw_polygon(x,y,4); ReleaseDC(0, hdc); } return 0;}畫線#
對于畫線部分,我這里使用了一個叫bresenham算法。。雖然我念不出名字,但是這個算法能夠幫助我們實現(xiàn)畫線運算,還有后面的中心圓填充,多邊形繪畫等方法。而且不通過浮點數(shù)的運算,直接變成整數(shù)運算,算法實現(xiàn)的函數(shù)如下所示,看起來比較簡單,運行速度也很快。
Copydef bresenham(x0, y0, x1, y1 , color): dx = abs(x1 - x0) dy = abs(y1 - y0) sx = 1 if x0 < x1 else -1 sy = 1 if y0 < y1 else -1 err = dx - dy while True: win32gui.SetPixel(dc, x0, y0, color) if x0 == x1 and y0 == y1: break e2 = 2 * err if e2 > -dy: err -= dy x0 += sx if e2 < dx: err += dx y0 += sy
我的屏幕分辨率是1920x1080的,只要在電腦里調用這個函數(shù),把兩個點的坐標填進去,就可以在顯示器屏幕上畫一條線。
中心圓算法#這個中心圓算法相對來說就比畫線的算法在理解上面難很多,但是實現(xiàn)起來更簡單一些,分成8個關于直線的和坐標軸對稱的區(qū)域畫圓,因此知道一個就可以畫出其他幾個,下面是實現(xiàn)過程。
Copydef draw_circle(x, y, r): x0 = 0 y0 = r d = 3 - 2 * r while x0 <= y0: win32gui.SetPixel(dc, x + x0, y + y0, 0xffffff) win32gui.SetPixel(dc, x + y0, y + x0, 0xffffff) win32gui.SetPixel(dc, x - y0, y + x0, 0xffffff) win32gui.SetPixel(dc, x - x0, y + y0, 0xffffff) win32gui.SetPixel(dc, x - x0, y - y0, 0xffffff) win32gui.SetPixel(dc, x - y0, y - x0, 0xffffff) win32gui.SetPixel(dc, x + y0, y - x0, 0xffffff) win32gui.SetPixel(dc, x + x0, y - y0, 0xffffff) if d < 0: d += 4 * x0 + 6 else: d += 4 * (x0 - y0) + 10 y0 -= 1 x0 += 1
在中心圓填充這里,可以取個巧,把幾個頂點直接用畫線的算法一行一行填充上去。就可以實現(xiàn)下面的效果。代碼如下
Copy# 畫實心圓def draw_circle_fill(x0, y0, r): x = 0 y = r d = 3 - 2 * r while x <= y: time.sleep(0.01) bresenham(x0 + x, y0 + y, x0 - x, y0 + y) time.sleep(0.01) bresenham(x0 + x, y0 - y, x0 - x, y0 - y) time.sleep(0.01) bresenham(x0 + y, y0 + x, x0 - y, y0 + x) time.sleep(0.01) bresenham(x0 + y, y0 - x, x0 - y, y0 - x) if d < 0: d += 4 * x + 6 else: d += 4 * (x - y) + 10 y -= 1 x += 1掃描線填充#
掃描線填充的算法就比較難實現(xiàn)了,需要找到起始的種子,還有每行的種子,因為我這里僅僅用頂點實現(xiàn)起來過于復雜,就索性偷懶用了數(shù)組。下面的算法實現(xiàn)部分僅供參考,具體的實現(xiàn)包括種子的選擇等等,可以更好一些。
Copymaps = [[0 for x in range(0,400)] for x in range(0,400)]for i in range(200,300): maps[i][200] = 1 maps[200][i] = 1 maps[i][300] = 1 maps[300][i] = 1for i in range(230,270): maps[i][230] = 1 maps[i][270] = 1 maps[230][i] = 1 maps[270][i] = 1# 掃描填充mapsdef scan_fill(): seed = (271,296) stack = [] stack.append(seed) while len(stack) > 0: (x,y) = stack.pop() # 如果已經被填充過,則跳過 if(maps[x][y] == 1): continue # 橫向填充并記錄lx rx i=0 time.sleep(0.01) while(maps[x+i][y] == 0): maps[x+i][y] = 1 win32gui.SetPixel(dc, x+i, y, 0xffffff) i += 1 rx = x+i-1 i=1 while(maps[x-i][y] == 0): maps[x-i][y] = 1 win32gui.SetPixel(dc, x-i, y, 0xffffff) i+=1 lx = x-i+1 # 下一個種子 if y+1>=300: continue i=0 while(maps[lx+i][y+1] == 0): if(maps[lx+i+1][y+1]==1): stack.append((lx+i,y+1)) break i+=1 i=0 while(maps[rx-i][y+1] == 0): if(maps[rx-i-1][y+1]==1): stack.append((rx-i,y+1)) break i+=1 if y-1<=0: continue i=0 while(maps[lx+i][y-1] == 0): if(maps[lx+i+1][y-1]==1): stack.append((lx+i,y-1)) break i+=1 i=0 while(maps[rx-i][y-1] == 0): if(maps[rx-i-1][y-1]==1): stack.append((rx-i,y-1)) break i+=1scan_fill()這里是所有代碼#
上面的代碼都是剪切過的,完整的代碼如下所示,運行后大家就可以在顯示器上看到運行過程:
Copyimport timeimport win32guidc = win32gui.GetDC(0)maps = [[0 for x in range(0,400)] for x in range(0,400)]for i in range(200,300): maps[i][200] = 1 maps[200][i] = 1 maps[i][300] = 1 maps[300][i] = 1for i in range(230,270): maps[i][230] = 1 maps[i][270] = 1 maps[230][i] = 1 maps[270][i] = 1# 中點算法畫圓def draw_circle(x, y, r): x0 = 0 y0 = r d = 3 - 2 * r while x0 <= y0: time.sleep(0.01) win32gui.SetPixel(dc, x + x0, y + y0, 0xffffff) time.sleep(0.01) win32gui.SetPixel(dc, x + y0, y + x0, 0xffffff) time.sleep(0.01) win32gui.SetPixel(dc, x - y0, y + x0, 0xffffff) time.sleep(0.01) win32gui.SetPixel(dc, x - x0, y + y0, 0xffffff) time.sleep(0.01) win32gui.SetPixel(dc, x - x0, y - y0, 0xffffff) time.sleep(0.01) win32gui.SetPixel(dc, x - y0, y - x0, 0xffffff) time.sleep(0.01) win32gui.SetPixel(dc, x + y0, y - x0, 0xffffff) time.sleep(0.01) win32gui.SetPixel(dc, x + x0, y - y0, 0xffffff) if d < 0: d += 4 * x0 + 6 else: d += 4 * (x0 - y0) + 10 y0 -= 1 x0 += 1# 畫線def bresenham(x0, y0, x1, y1): dx = abs(x1 - x0) dy = abs(y1 - y0) sx = 1 if x0 < x1 else -1 sy = 1 if y0 < y1 else -1 err = dx - dy while True: #time.sleep(0.01) win32gui.SetPixel(dc, x0, y0, 0xffffff) if x0 == x1 and y0 == y1: break e2 = 2 * err if e2 > -dy: err -= dy x0 += sx if e2 < dx: err += dx y0 += sy# 畫實心圓def draw_circle_fill(x0, y0, r): x = 0 y = r d = 3 - 2 * r while x <= y: time.sleep(0.01) bresenham(x0 + x, y0 + y, x0 - x, y0 + y) time.sleep(0.01) bresenham(x0 + x, y0 - y, x0 - x, y0 - y) time.sleep(0.01) bresenham(x0 + y, y0 + x, x0 - y, y0 + x) time.sleep(0.01) bresenham(x0 + y, y0 - x, x0 - y, y0 - x) if d < 0: d += 4 * x + 6 else: d += 4 * (x - y) + 10 y -= 1 x += 1# 畫多邊形def draw_polygon(points): for i in range(len(points)): x0 = points[i][0] y0 = points[i][1] x1 = points[(i + 1) % len(points)][0] y1 = points[(i + 1) % len(points)][1] bresenham(x0, y0, x1, y1)# 畫橢圓def draw_ellipse(x0, y0, a, b): x = 0 y = b a2 = a * a b2 = b * b d = b2 - a2 * b + a2 / 4 while b2 * x <= a2 * y: win32gui.SetPixel(dc, x0 + x, y0 + y, 0xffffff) win32gui.SetPixel(dc, x0 - x, y0 + y, 0xffffff) win32gui.SetPixel(dc, x0 + x, y0 - y, 0xffffff) win32gui.SetPixel(dc, x0 - x, y0 - y, 0xffffff) if d < 0: d += b2 * (2 * x + 3) else: d += b2 * (2 * x - 2 * y + 5) y -= 1 x += 1 d1 = b2 * (x + 0.5) * (x + 0.5) + a2 * (y - 1) * (y - 1) - a2 * b2 while y >= 0: win32gui.SetPixel(dc, x0 + x, y0 + y, 0xffffff) win32gui.SetPixel(dc, x0 - x, y0 + y, 0xffffff) win32gui.SetPixel(dc, x0 + x, y0 - y, 0xffffff) win32gui.SetPixel(dc, x0 - x, y0 - y, 0xffffff) if d1 > 0: d1 -= a2 * (2 * y - 1) d1 += b2 * (2 * x + 3) x += 1 y -= 1# 畫矩形def draw_rectangle(x0, y0, x1, y1): bresenham(x0, y0, x1, y0) bresenham(x1, y0, x1, y1) bresenham(x1, y1, x0, y1) bresenham(x0, y1, x0, y0)# 掃描填充mapsdef scan_fill(): seed = (271,296) stack = [] stack.append(seed) while len(stack) > 0: (x,y) = stack.pop() # 如果已經被填充過,則跳過 if(maps[x][y] == 1): continue # 橫向填充并記錄lx rx i=0 time.sleep(0.01) while(maps[x+i][y] == 0): maps[x+i][y] = 1 win32gui.SetPixel(dc, x+i, y, 0xffffff) i += 1 rx = x+i-1 i=1 while(maps[x-i][y] == 0): maps[x-i][y] = 1 win32gui.SetPixel(dc, x-i, y, 0xffffff) i+=1 lx = x-i+1 # 下一個種子 if y+1>=300: continue i=0 while(maps[lx+i][y+1] == 0): if(maps[lx+i+1][y+1]==1): stack.append((lx+i,y+1)) break i+=1 i=0 while(maps[rx-i][y+1] == 0): if(maps[rx-i-1][y+1]==1): stack.append((rx-i,y+1)) break i+=1 if y-1<=0: continue i=0 while(maps[lx+i][y-1] == 0): if(maps[lx+i+1][y-1]==1): stack.append((lx+i,y-1)) break i+=1 i=0 while(maps[rx-i][y-1] == 0): if(maps[rx-i-1][y-1]==1): stack.append((rx-i,y-1)) break i+=1scan_fill()while True: # 畫線 bresenham(400, 900, 1000, 700) # 填充圓 draw_circle_fill(900, 500, 100) # 中心圓 draw_circle(1000, 200, 100) # 橢圓 draw_ellipse(1500, 200, 100, 100) # 矩形 draw_rectangle(1100, 400, 1200, 500) # 多邊形 draw_polygon([(900, 1000), (800, 800), (1000, 900), (1100, 1000)]) #三角形 draw_polygon([(400, 200), (500, 300), (600, 200)])裁剪#
裁剪這里簡直就是我的噩夢,因為我之前為了極客選擇了僅僅知道頂點就畫出裁剪過的多邊形,導致我沒有數(shù)組,只能設計更極客的算法。最后我找到了一種裁剪凸多邊形的辦法,大致就是找到每個線段的交點,然后順時針方向對交點和在主多邊形,副多邊形的頂點排序,最后就可以實現(xiàn)裁剪。代碼超級復雜,
Copyimport win32guiimport mathimport pygamedc = win32gui.GetDC(0)# 獲取鼠標的位置mouse_x=win32gui.GetCursorPos()[0]mouse_y=win32gui.GetCursorPos()[1]temp = win32gui.GetCursorPos()def get_mouse_pos(): global mouse_x, mouse_y mouse_x = win32gui.GetCursorPos()[0] mouse_y = win32gui.GetCursorPos()[1]clock = pygame.time.Clock()temp2 = []def bresenham(x0, y0, x1, y1 , color): dx = abs(x1 - x0) dy = abs(y1 - y0) sx = 1 if x0 < x1 else -1 sy = 1 if y0 < y1 else -1 err = dx - dy while True: win32gui.SetPixel(dc, x0, y0, color) if x0 == x1 and y0 == y1: break e2 = 2 * err if e2 > -dy: err -= dy x0 += sx if e2 < dx: err += dx y0 += sydef draw_rectangle(x0, y0, x1, y1): bresenham(x0, y0, x1, y0,0xffffff) bresenham(x1, y0, x1, y1,0xffffff) bresenham(x1, y1, x0, y1,0xffffff) bresenham(x0, y1, x0, y0,0xffffff)def draw_polygon(points): for i in range(len(points)): x0 = points[i][0] y0 = points[i][1] x1 = points[(i + 1) % len(points)][0] y1 = points[(i + 1) % len(points)][1] bresenham(x0, y0, x1, y1,0x00ff00)def draw_polygon_black(points): for i in range(len(points)): x0 = points[i][0] y0 = points[i][1] x1 = points[(i + 1) % len(points)][0] y1 = points[(i + 1) % len(points)][1] bresenham(x0, y0, x1, y1,0x000000)# 線段是否相交def IsRectCross(p1x, p1y, p2x, p2y, q1x, q1y, q2x, q2y): return min(p1x,p2x) <= max(q1x,q2x) and min(q1x,q2x) <= max(p1x,p2x) and min(p1y,p2y) <= max(q1y,q2y) and min(q1y,q2y) <= max(p1y,p2y)def IsLineSegmentCross(pFirst1x,pFirst1y,pFirst2x,pFirst2y,pSecond1x,pSecond1y,pSecond2x,pSecond2y): line1 = pFirst1x * (pSecond1y - pFirst2y) + pFirst2x * (pFirst1y - pSecond1y) + pSecond1x * (pFirst2y - pFirst1y) line2 = pFirst1x * (pSecond2y - pFirst2y) + pFirst2x * (pFirst1y - pSecond2y) + pSecond2x * (pFirst2y - pFirst1y) if (((line1 ^ line2) >= 0) and not (line1 == 0 and line2 == 0)): return False line1 = pSecond1x * (pFirst1y - pSecond2y) + pSecond2x * (pSecond1y - pFirst1y) + pFirst1x * (pSecond2y - pSecond1y) line2 = pSecond1x * (pFirst2y - pSecond2y) + pSecond2x * (pSecond1y - pFirst2y) + pFirst2x * (pSecond2y - pSecond1y) if (((line1 ^ line2) >= 0) and not (line1 == 0 and line2 == 0)): return False return Truedef GetCrossPoint(p1x, p1y, p2x, p2y, q1x, q1y, q2x, q2y): if(IsRectCross(p1x, p1y, p2x, p2y, q1x, q1y, q2x, q2y)): if (IsLineSegmentCross(p1x, p1y, p2x, p2y, q1x, q1y, q2x, q2y)): tmpLeft = (q2x - q1x) * (p1y - p2y) - (p2x - p1x) * (q1y - q2y) tmpRight = (p1y - q1y) * (p2x - p1x) * (q2x - q1x) + q1x * (q2y - q1y) * (p2x - p1x) - p1x * (p2y - p1y) * (q2x - q1x) if (tmpLeft == 0): return None x = (int)(tmpRight/tmpLeft) tmpLeft = (p1x - p2x) * (q2y - q1y) - (p2y - p1y) * (q1x - q2x) tmpRight = p2y * (p1x - p2x) * (q2y - q1y) + (q2x- p2x) * (q2y - q1y) * (p1y - p2y) - q2y * (q1x - q2x) * (p2y - p1y) if (tmpLeft == 0): return None y = (int)(tmpRight/tmpLeft) return (x,y) else: return None else: return Nonedef draw_rectangle_black(x0, y0, x1, y1): bresenham(x0, y0, x1, y0,0x000000) bresenham(x1, y0, x1, y1,0x000000) bresenham(x1, y1, x0, y1,0x000000) bresenham(x0, y1, x0, y0,0x000000)# 判斷點是否在多邊形內def IsPointInPolygon(points, x, y): nCross = 0 for i in range(len(points)): p1x = points[i][0] p1y = points[i][1] p2x = points[(i + 1) % len(points)][0] p2y = points[(i + 1) % len(points)][1] if (y > min(p1y, p2y)): if (y <= max(p1y, p2y)): if (x <= max(p1x, p2x)): if (p1y != p2y): xinters = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x if (p1x == p2x or x <= xinters): nCross += 1 if (nCross % 2 == 0): return False else: return Truedef getNonRepeatList(data): new_data = [] for i in range(len(data)): if data[i] not in new_data: new_data.append(data[i]) return new_data# 判斷兩多邊形重疊部分 返回一個多邊形def IsPolygonCross(points1, points2): result = [] for i in range(len(points1)): p1x = points1[i][0] p1y = points1[i][1] p2x = points1[(i + 1) % len(points1)][0] p2y = points1[(i + 1) % len(points1)][1] for j in range(len(points2)): q1x = points2[j][0] q1y = points2[j][1] q2x = points2[(j + 1) % len(points2)][0] q2y = points2[(j + 1) % len(points2)][1] if (IsPointInPolygon(points1, q1x, q1y) and (q1x, q1y) not in result): result.append((q1x, q1y)) if (IsPointInPolygon(points1, q2x, q2y) and (q2x, q2y) not in result): result.append((q2x, q2y)) if (IsPointInPolygon(points2, p1x, p1y) and (p1x, p1y) not in result): result.append((p1x, p1y)) if (IsPointInPolygon(points2, p2x, p2y) and (p2x, p2y) not in result): result.append((p2x, p2y)) if (IsRectCross(p1x, p1y, p2x, p2y, q1x, q1y, q2x, q2y)): if GetCrossPoint(p1x, p1y, p2x, p2y, q1x, q1y, q2x, q2y) != None: (x, y) = GetCrossPoint(p1x, p1y, p2x, p2y, q1x, q1y, q2x, q2y) result.append((x, y)) if (result == []): return result return (sort_points_in_clockwise_order(result))w = 60h = 100def draw_polygon_red(points): for i in range(len(points)): x0 = points[i][0] y0 = points[i][1] x1 = points[(i + 1) % len(points)][0] y1 = points[(i + 1) % len(points)][1] bresenham(x0, y0, x1, y1,0xff0000)def sort_points_in_clockwise_order(points): center = (0, 0) for point in points: center = (center[0] + point[0], center[1] + point[1]) center = (center[0] / len(points), center[1] / len(points)) points_copy = list(points) points_copy.sort(key=lambda point: math.atan2(point[0] - center[0], point[1] - center[1])) res = [] for i in points_copy: res.append((i[0]+500,i[1])) return respolygon_Points = [(600,500), (800,500), (900, 600), (900, 400),(600,300)]while True: draw_rectangle_black(temp[0],temp[1],temp[0]+w,temp[1]+h) draw_rectangle(mouse_x,mouse_y,mouse_x+w,mouse_y+h) temp = (mouse_x,mouse_y) get_mouse_pos() draw_polygon(polygon_Points) res = IsPolygonCross(polygon_Points,[(mouse_x,mouse_y),(mouse_x+w,mouse_y),(mouse_x+w,mouse_y+h),(mouse_x,mouse_y+h)]) print(res) if temp2 != []: draw_polygon_black(temp2) if res != [] and res != None: draw_polygon_red(res) temp2 = res clock.tick(120)# 60幀 總結#
計算機圖形學并沒有我之前想的那么好學,踩了很多坑,也補了很多知識。希望后面能再接再厲
以上就是關于pos機器動圖,基于python win32setpixel api 實現(xiàn)計算機圖形學相關操作的知識,后面我們會繼續(xù)為大家整理關于pos機器動圖的知識,希望能夠幫助到大家!