Advertisement
glerium

heart-py

Dec 25th, 2022
51
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.63 KB | None | 0 0
  1. from tkinter import *
  2. from matplotlib import pyplot as plt
  3. from PIL import Image
  4. import random
  5. import math
  6. import numpy as np
  7. import os
  8. import colorsys
  9. import cv2
  10. from scipy.ndimage.filters import gaussian_filter
  11.  
  12. canvas_width = 600
  13. canvas_height = 600
  14. world_width = 0.05
  15. world_heigth = 0.05
  16.  
  17. # 中间心的参数
  18. points = None
  19. fixed_point_size = 20000
  20. fixed_scale_range = (4, 4.3)
  21. min_scale = np.array([1.0, 1.0, 1.0]) * 0.9
  22. max_scale = np.array([1.0, 1.0, 1.0]) * 0.9
  23. min_heart_scale = -15
  24. max_heart_scale = 16
  25.  
  26. # 外围随机心参数
  27. random_point_szie = 7000
  28. random_scale_range = (3.5, 3.9)
  29. random_point_maxvar = 0.2
  30.  
  31. # 心算法参数
  32. mid_point_ignore = 0.95
  33.  
  34. # 相机参数
  35. camera_close_plane = 0.1
  36. camera_position = np.array([0.0, -2.0, 0.0])
  37.  
  38. # 点的颜色
  39. hue = 0.60
  40. color_strength = 255
  41.  
  42. # 常用向量缓存
  43. zero_scale = np.array([0.0, 0.0, 0.0])
  44. unit_scale = np.array([1.0, 1.0, 1.0])
  45. color_white = np.array([255, 255, 255])
  46. axis_y = np.array([0.0, 1.0, 0.0])
  47.  
  48. # 渲染缓存
  49. render_buffer = np.empty((canvas_width, canvas_height, 3), dtype=int)
  50. strength_buffer = np.empty((canvas_width, canvas_height), dtype=float)
  51.  
  52. # 随机点文件缓存
  53. points_file = "temp.txt"
  54.  
  55. # 渲染结果
  56. total_frames = 30
  57. output_dir = "./output"
  58.  
  59. # 格式
  60. image_fmt = "jpg"
  61.  
  62. def color(value):
  63.     digit = list(map(str, range(10))) + list("ABCDEF")
  64.     string = '#'
  65.     for i in value:
  66.         a1 = i // 16
  67.         a2 = i % 16
  68.         string += digit[a1] + digit[a2]
  69.     return string
  70.    
  71.  
  72. def heart_func(x, y, z, scale):
  73.     bscale = scale
  74.     bscale_half = bscale / 2
  75.     x = x * bscale - bscale_half
  76.     y = y * bscale - bscale_half
  77.     z = z * bscale - bscale_half
  78.     return (x**2 + 9/4*(y**2) + z**2 - 1)**3 - (x**2)*(z**3) - 9/200*(y**2)*(z**3)
  79.  
  80. def lerp_vector(a, b, ratio):
  81.     result = a.copy()
  82.     for i in range(3):
  83.         result[i] = a[i] + (b[i] - a[i]) * ratio
  84.     return result
  85.  
  86. def lerp_int(a, b, ratio):
  87.     return (int)(a + (b - a) * ratio)
  88.  
  89. def lerp_float(a, b, ratio):
  90.     return (a + (b - a) * ratio)
  91.  
  92. def distance(point):
  93.     return (point[0]**2 + point[1]**2 + point[2]**2) ** 0.5
  94.  
  95. def dot(a, b):
  96.     return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]
  97.  
  98. def inside_rand(tense):
  99.     x = random.random()
  100.     y = -tense * math.log(x)
  101.     return y
  102.  
  103. # 生成中间心
  104. def genPoints(pointCount, heartScales):
  105.     result = np.empty((pointCount, 3))
  106.     index = 0
  107.     while index < pointCount:
  108.         # 生成随机点
  109.         x = random.random()
  110.         y = random.random()
  111.         z = random.random()
  112.  
  113.         # 扣掉心中间的点
  114.         mheartValue = heart_func(x, 0.5, z, heartScales[1])
  115.         mid_ignore = random.random()
  116.         if mheartValue < 0 and mid_ignore < mid_point_ignore:
  117.             continue
  118.        
  119.         heartValue = heart_func(x, y, z, heartScales[0])
  120.         z_shrink = 0.01
  121.         sz = z - z_shrink
  122.         sheartValue = heart_func(x, y, sz, heartScales[1])
  123.  
  124.         # 保留在心边上的点
  125.         if heartValue < 0 and sheartValue > 0:
  126.             result[index] = [x - 0.5, y - 0.5, z - 0.5]
  127.  
  128.             # 向内扩散
  129.             len = 0.7
  130.             result[index] = result[index] * (1 - len * inside_rand(0.2))
  131.  
  132.             # 重新赋予深度
  133.             newY = random.random() - 0.5
  134.             rheartValue = heart_func(result[index][0] + 0.5, newY + 0.5, result[index][2] + 0.5, heartScales[0])
  135.             if rheartValue > 0:
  136.                 continue
  137.             result[index][1] = newY
  138.  
  139.             # 删掉肚脐眼
  140.             dist = distance(result[index])
  141.             if dist < 0.12:
  142.                 continue
  143.            
  144.             index = index + 1
  145.             if index % 100 == 0:
  146.                 print("{ind} generated {per}%".format(ind=index, per=((index / pointCount) * 100)))
  147.  
  148.     return result
  149.  
  150. # 生成随机心
  151. def genRandPoints(pointCount, heartScales, maxVar, ratio):
  152.     result = np.empty((pointCount, 3))
  153.     index = 0
  154.     while index < pointCount:
  155.         x = random.random()
  156.         y = random.random()
  157.         z = random.random()
  158.         mheartValue = heart_func(x, 0.5, z, heartScales[1])
  159.         mid_ignore = random.random()
  160.         if mheartValue < 0 and mid_ignore < mid_point_ignore:
  161.             continue
  162.  
  163.         heartValue = heart_func(x, y, z, heartScales[0])
  164.         sheartValue = heart_func(x, y, z, heartScales[1])
  165.  
  166.         if heartValue < 0 and sheartValue > 0:
  167.             result[index] = [x - 0.5, y - 0.5, z - 0.5]
  168.             dist = distance(result[index])
  169.             if dist < 0.12:
  170.                 continue
  171.  
  172.             len = 0.7
  173.             result[index] = result[index] * (1 - len * inside_rand(0.2))
  174.             index = index + 1
  175.  
  176.     for i in range(pointCount):
  177.         var = maxVar * ratio
  178.         randScale = 1 + random.normalvariate(0, var)
  179.         result[i] = result[i] * randScale
  180.  
  181.     return result
  182.  
  183. # 世界坐标到相机本地坐标
  184. def world_2_cameraLocalSapce(world_point):
  185.     new_point = world_point.copy()
  186.     new_point[1] = new_point[1] + camera_position[1]
  187.     return new_point
  188.  
  189. # 相机本地坐标到相机空间坐标
  190. def cameraLocal_2_cameraSpace(cameraLocalPoint):
  191.     depth = distance(cameraLocalPoint)
  192.     cx = cameraLocalPoint[0] * (camera_close_plane / cameraLocalPoint[1])
  193.     cz = -cameraLocalPoint[2] * (cx / cameraLocalPoint[0])
  194.     cameraLocalPoint[0] = cx
  195.     cameraLocalPoint[1] = cz
  196.     return cameraLocalPoint, depth
  197.  
  198. # 相机空间坐标到屏幕坐标
  199. def camerSpace_2_screenSpace(cameraSpace):
  200.     x = cameraSpace[0]
  201.     y = cameraSpace[1]
  202.  
  203.     # convert to view space
  204.     centerx = canvas_width / 2
  205.     centery = canvas_height / 2
  206.     ratiox = canvas_width / world_width
  207.     ratioy = canvas_height / world_heigth
  208.  
  209.     viewx = centerx + x * ratiox
  210.     viewy = canvas_height - (centery + y * ratioy)
  211.  
  212.     cameraSpace[0] = viewx
  213.     cameraSpace[1] = viewy
  214.     return cameraSpace.astype(int)
  215.  
  216.  
  217. # 绘制世界坐标下的点
  218. def draw_point(worldPoint):
  219.     cameraLocal = world_2_cameraLocalSapce(worldPoint)
  220.     cameraSpsace, depth = cameraLocal_2_cameraSpace(cameraLocal)
  221.     screeSpace = camerSpace_2_screenSpace(cameraSpsace)
  222.  
  223.     draw_size = int(random.random() * 3 + 1)
  224.     draw_on_buffer(screeSpace, depth, draw_size)
  225.  
  226. # 绘制到缓存上
  227. def draw_on_buffer(screenPos, depth, draw_size):
  228.     if draw_size == 0:
  229.         return
  230.     elif draw_size == 1:
  231.         draw_point_on_buffer(screenPos[0], screenPos[1], color_strength, depth)
  232.     elif draw_size == 2:
  233.         draw_point_on_buffer(screenPos[0], screenPos[1], color_strength, depth)
  234.         draw_point_on_buffer(screenPos[0] + 1, screenPos[1] + 1, color_strength, depth)
  235.     elif draw_size == 3:
  236.         draw_point_on_buffer(screenPos[0], screenPos[1], color_strength, depth)
  237.         draw_point_on_buffer(screenPos[0] + 1, screenPos[1] + 1, color_strength, depth)
  238.         draw_point_on_buffer(screenPos[0] + 1, screenPos[1], color_strength, depth)
  239.     elif draw_size == 4:
  240.         draw_point_on_buffer(screenPos[0], screenPos[1], color_strength, depth)
  241.         draw_point_on_buffer(screenPos[0] + 1, screenPos[1], color_strength, depth)
  242.         draw_point_on_buffer(screenPos[0], screenPos[1] + 1, color_strength, depth)
  243.         draw_point_on_buffer(screenPos[0] + 1, screenPos[1] + 1, color_strength, depth)
  244.  
  245.  
  246. # 根据色调和颜色强度获取颜色
  247. def get_color(strength):
  248.     result = None
  249.     if strength >= 1:
  250.         result = colorsys.hsv_to_rgb(hue, 2 - strength, 1)
  251.     else:
  252.         result = colorsys.hsv_to_rgb(hue, 1, strength)
  253.     r = min(result[0] * 256, 255)
  254.     g = min(result[1] * 256, 255)
  255.     b = min(result[2] * 256, 255)
  256.     return np.array((r, g, b), dtype=int)
  257.  
  258. # 可以根据深度做一些好玩的
  259. def draw_point_on_buffer(x, y, color, depth):
  260.     if x < 0 or x >= canvas_width or y < 0 or y >= canvas_height:
  261.         return
  262.  
  263.     # 混合
  264.     strength = float(color) / 255
  265.     strength_buffer[x, y] = strength_buffer[x, y] + strength
  266.  
  267. # 绘制缓存
  268. def draw_buffer_on_canvas(output = None):
  269.     render_buffer.fill(0)
  270.     for i in range(render_buffer.shape[0]):
  271.         for j in range(render_buffer.shape[1]):
  272.             render_buffer[i, j] = get_color(strength_buffer[i, j])
  273.     im = Image.fromarray(np.uint8(render_buffer))
  274.     im = im.rotate(-90)
  275.     if output is None:
  276.         plt.imshow(im)
  277.         plt.show()
  278.     else:
  279.         im.save(output)
  280.  
  281.  
  282. def paint_heart(ratio, randratio, outputFile = None):
  283.     global strength_buffer
  284.     global render_buffer
  285.     global points
  286.  
  287.     # 清空缓存
  288.     strength_buffer.fill(0)
  289.  
  290.     for i in range(fixed_point_size):
  291.         # 缩放
  292.         point = points[i] * lerp_vector(min_scale, max_scale, ratio)
  293.  
  294.         # 球型场
  295.         dist = distance(point)
  296.         radius = 0.4
  297.         sphere_scale = radius / dist
  298.         point = point * lerp_float(0.9, sphere_scale, ratio * 0.3)
  299.  
  300.         # 绘制
  301.         draw_point(point)
  302.  
  303.     # 生成一组随机点
  304.     randPoints = genRandPoints(random_point_szie, random_scale_range, random_point_maxvar, randratio)
  305.     for i in range(random_point_szie):
  306.         # 绘制
  307.         draw_point(randPoints[i])
  308.  
  309.     # 高斯模糊
  310.     for i in range(1):
  311.         strength_buffer = gaussian_filter(strength_buffer, sigma=0.8)
  312.  
  313.     # 绘制缓存
  314.     draw_buffer_on_canvas(outputFile)
  315.  
  316. def show_images():
  317.     img = None
  318.     for i in range(total_frames):
  319.         save_name = "{name}.{fmt}".format(name=i, fmt=image_fmt)
  320.         save_path = os.path.join(output_dir, save_name)
  321.         img = cv2.imread(save_path, cv2.IMREAD_ANYCOLOR)
  322.         cv2.imshow("Img", img)
  323.         cv2.waitKey(25)
  324.  
  325.  
  326. def gen_images():
  327.     global points
  328.  
  329.     if not os.path.isdir(output_dir):
  330.         os.mkdir(output_dir)
  331.    
  332.     # 尝试加载或生成中间心
  333.     if not os.path.exists(points_file):
  334.         print("未发现缓存点,重新生成中")
  335.         points = genPoints(fixed_point_size, fixed_scale_range)
  336.         np.savetxt(points_file, points)
  337.     else:
  338.         print("发现缓存文件,跳过生成")
  339.         points = np.loadtxt(points_file)
  340.  
  341.     for i in range(total_frames):
  342.         print("正在处理图片... ", i)
  343.         frame_ratio = float(i) / (total_frames - 1)
  344.         frame_ratio = frame_ratio ** 2
  345.         ratio = math.sin(frame_ratio * math.pi) * 0.743144
  346.         randratio = math.sin(frame_ratio * math.pi * 2 + total_frames / 2)
  347.         save_name = "{name}.{fmt}".format(name=i, fmt=image_fmt)
  348.         save_path = os.path.join(output_dir, save_name)
  349.         paint_heart(ratio, randratio, save_path)
  350.         print("图片已保存至", save_path)
  351.  
  352.  
  353. if __name__ == "__main__":
  354.     gen_images()
  355.     while True:
  356.         show_images()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement