最近读研期间上了计算机可视化的课,老师也对计算机图形学的实现布置了相关的作业。虽然我没有系统地学过图形可视化的课,但是我之前逆向过一些游戏引擎,除了保护驱动之外,因为要做透视,接触过一些计算机图形学的基础常识。这次的作业主要分为2个主要模块,一个是实现画线,画圆的算法,还有填充的算法,以及裁剪的算法。

之前工作的时候虽然参与过一些数据可视化大屏的设计,但是当时主要的工作使用Echarts或者G2做业务组件开发,并没有对画线,填充,裁剪等基础算法做过实现。这次就着这个机会我就想了解一些。实现的效果如下(动图加载可能有些慢):

扫描线填充过程

裁剪过程(根据鼠标位置,实时裁剪多边形,右边的蓝色是裁剪后的图形)

为什么选择win32 api画图

选择win32的原因是我想做一些与众不同的实现方法,比起用D3或者Echarts这种webGL的实现方式,我更想直接在显示器上画出图像,看起来更极客一些。这也导致了录屏软件没办法捕捉,只能用手机来录制

为什么不选C++而选择python

主要是python能对内存做个管理,C++直接调这种底层的接口会把内存搞坏掉,导致电脑变得特别卡。。不信大家可以在电脑上编译运行这段代码2分钟试一试,如果你电脑没炸,算你有钱。。

#include <windows.h>
// g++ a.cpp -o a.exe -lgdi32 && a.exe
void 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算法。。虽然我念不出名字,但是这个算法能够帮助我们实现画线运算,还有后面的中心圆填充,多边形绘画等方法。而且不通过浮点数的运算,直接变成整数运算,算法实现的函数如下所示,看起来比较简单,运行速度也很快。

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 += sy

我的屏幕分辨率是1920x1080的,只要在电脑里调用这个函数,把两个点的坐标填进去,就可以在显示器屏幕上画一条线。

中心圆算法

这个中心圆算法相对来说就比画线的算法在理解上面难很多,但是实现起来更简单一些,分成8个关于直线的和坐标轴对称的区域画圆,因此知道一个就可以画出其他几个,下面是实现过程。

def 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



在中心圆填充这里,可以取个巧,把几个顶点直接用画线的算法一行一行填充上去。就可以实现下面的效果。代码如下

# 画实心圆
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

扫描线填充

扫描线填充的算法就比较难实现了,需要找到起始的种子,还有每行的种子,因为我这里仅仅用顶点实现起来过于复杂,就索性偷懒用了数组。下面的算法实现部分仅供参考,具体的实现包括种子的选择等等,可以更好一些。

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] = 1
for i in range(230,270):
maps[i][230] = 1
maps[i][270] = 1
maps[230][i] = 1
maps[270][i] = 1
# 扫描填充maps
def 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+=1
scan_fill()

这里是所有代码

上面的代码都是剪切过的,完整的代码如下所示,运行后大家就可以在显示器上看到运行过程:

import time
import win32gui
dc = 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] = 1
for 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) # 扫描填充maps
def 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+=1
scan_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)])

裁剪

裁剪这里简直就是我的噩梦,因为我之前为了极客选择了仅仅知道顶点就画出裁剪过的多边形,导致我没有数组,只能设计更极客的算法。

最后我找到了一种裁剪凸多边形的办法,大致就是找到每个线段的交点,然后顺时针方向对交点和在主多边形,副多边形的顶点排序,最后就可以实现裁剪。代码超级复杂,

import win32gui
import math
import pygame
dc = 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 += sy def 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 True def 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 None def 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 True
def 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 = 60
h = 100 def 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 res polygon_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帧

总结

计算机图形学并没有我之前想的那么好学,踩了很多坑,也补了很多知识。希望后面能再接再厉

基于python win32setpixel api 实现计算机图形学相关操作的更多相关文章

  1. python文件、文件夹的相关操作

    python文件.文件夹的相关操作 #1.rename()可以完成对文件的重命名 #rename(需要修改的文件名,新的文件名) import os os.rename("readme.tx ...

  2. python基础===【字符串】所有相关操作

    #字符串的相关操作 #基本操作 #+ 字符串连接操作 str1 = '来是come走是go' str2 = '点头yes摇头no' result = str1 + str2 print(result) ...

  3. python、第二篇:库相关操作

    一 系统数据库 information_schema: 虚拟库,不占用磁盘空间,存储的是数据库启动后的一些参数,如用户表信息.列信息.权限信息.字符信息等performance_schema: MyS ...

  4. Python之数据加密与解密及相关操作(hashlib模块、hmac模块、random模块、base64模块、pycrypto模块)

    本文内容 数据加密概述 Python中实现数据加密的模块简介 hashlib与hmac模块介绍 random与secrets模块介绍 base64模块介绍 pycrypto模块介绍 总结 参考文档 提 ...

  5. Python之数据加密与解密及相关操作(hashlib、hmac、random、base64、pycrypto)

    本文内容 数据加密概述 Python中实现数据加密的模块简介 hashlib与hmac模块介绍 random与secrets模块介绍 base64模块介绍 pycrypto模块介绍 总结 参考文档 提 ...

  6. python 零散记录(二) 序列的相关操作 相加 相乘 改变 复制

    序列相加: [1,2] + [3,4] == [1,2,3,4] #字符串也是序列的一种 'hello' + 'world' == 'hello world' #但是序列相加只限于相同类型的序列间相加 ...

  7. python基础学习一 字符串的相关操作

    python的字符串 在python中,字符串是以unicode编码的,所以python的字符串支持多语言 对于单个字符的编码,python提供了ord()函数获取字符的整数表示,chr()函数是把编 ...

  8. python(列表及列表的相关操作、元组和range)

    1.什么是列表 列表是一个课表的数据类型 列表有[]来表示,每一项元素用逗号隔开,列表什么都能装.是能装对象的对象. 列表可以装大量数据. 2.列表的索引和切片 列表和字符串一样,也有索引和切片.只不 ...

  9. SIGGRAPH 2017:深度学习与计算机图形学的碰撞

    每年由美国计算机协会(Association of Computing Machinery,简称ACM)计算机图形专业组举办的年会SIGGRAPH,是全球最负盛名的图形学和交互技术盛会.今年已经是这场 ...

随机推荐

  1. linux基本命令二

    组管理与权限管理  文件/目录所有者  修改文件所有者 chown 用户名 文件名 创建文件所在组  groupadd 修改文件所在组 chgrp  组名  文件名 其他组:除文件的所有者和所在组的用 ...

  2. 攻防世界 Misc 新手练习区 gif Writeup

    攻防世界 Misc 新手练习区 gif Writeup 题目介绍 题目考点 仔细联想 字符转换 Writeup 下载附件并打开 104张黑白图 发现是一堆黑色和白色的图片,按某种规律排列,猜想flag ...

  3. thread pool

    thread pool import concurrent.futures import urllib.request URLS = ['http://www.foxnews.com/', 'http ...

  4. 《Python语言程序设计》【第3周】基本数据类型

    实例3:天天向上的力量 #DayDayUpQ1.py dayup = pow(1.001,365) daydown = pow(0.999,365) print("向上: {:.2f},向下 ...

  5. UE4中C++编程(一)

    一: C++工程和Gameplay框架 GameInstance 它适合放置独立于关卡的信息,比如说显示UI. GameMode 表示游戏玩法, 包含游戏进行的规则和胜利条件等等信息,游戏模式是和关卡 ...

  6. [源码解析] PyTorch分布式(5) ------ DistributedDataParallel 总述&如何使用

    [源码解析] PyTorch 分布式(5) ------ DistributedDataParallel 总述&如何使用 目录 [源码解析] PyTorch 分布式(5) ------ Dis ...

  7. vue+node+mongondb实战之路由

    看了一段时间vue的文档,一直没有机会来开发一个真正的vue项目,趁着这几天清闲,整合一下最新的技术,变学变练来开发一个vue的简单博客 有了开发博客的想法之后,谁知道第一步就被拦住了,看了vue的基 ...

  8. java及python调用RabbitMQ

    1,python调用MQ发送消息(生产者),话不多说,直接上干货 import pika 如下图 2.java调用MQ发送消息(生产者) 具体代码如下: python 的代码如下 connection ...

  9. Redis | 第4章 Redis中的数据库《Redis设计与实现》

    目录 前言 1. Redis中的数据库 2. 数据库的键空间 3. 键的生成时间与过期时间 4. Redis中的过期键删除策略 5. AOF.RDB和复制功能对过期键的处理 5.1 生成 RDB 文件 ...

  10. springboot默认Thymeleaf模板引擎js的解决方案

    <script th:inline="javascript"> var btnexam=[[${btnexam}]]; console.log(btnexam); va ...