larry77

Untitled

May 14th, 2026
22
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.96 KB | None | 0 0
  1. #!/usr/bin/env python3
  2.  
  3. ###############################################################################
  4. # Static PNG plot of two jellyfish-like creatures
  5. #
  6. # This script computes one static frame and saves it as a PNG.
  7. ###############################################################################
  8.  
  9. import numpy as np
  10. import matplotlib.pyplot as plt
  11.  
  12. # =============================================================================
  13. # 1. Tunable parameters
  14. # =============================================================================
  15.  
  16. n_points = 20_000
  17.  
  18. # Integer frame number: 1, 2, 3, ...
  19. #
  20. # It is converted inside jellyfish_coordinates() to:
  21. #
  22. # t = frame_id * pi / 45
  23. frame_id = 1
  24.  
  25. canvas_width = 400
  26. canvas_height = 400
  27.  
  28. render_width_px = 1600
  29. render_height_px = 1600
  30.  
  31. dpi = 100
  32.  
  33. background_colour = "#090909"
  34. point_colour = "white"
  35. point_alpha = 0.70
  36.  
  37. # In matplotlib, s is marker area in points squared.
  38. point_size = 1.5
  39.  
  40. output_file = "jellyfish_frame.png"
  41.  
  42. # =============================================================================
  43. # 2. Function returning x/y coordinates for a given frame
  44. # =============================================================================
  45.  
  46. def jellyfish_coordinates(frame_id, n_points=20_000):
  47. """
  48. Compute the x/y coordinates of the two jellyfish-like creatures.
  49.  
  50. Parameters
  51. ----------
  52. frame_id : int
  53. Integer frame number: 1, 2, 3, ...
  54.  
  55. The frame number is converted to continuous time as:
  56.  
  57. t = frame_id * pi / 45
  58.  
  59. This mirrors the original JavaScript update:
  60.  
  61. t += PI / 45
  62.  
  63. once per draw call.
  64.  
  65. n_points : int
  66. Number of points in the point cloud.
  67.  
  68. Returns
  69. -------
  70. x : numpy.ndarray
  71. X coordinates of all points.
  72.  
  73. y : numpy.ndarray
  74. Y coordinates of all points.
  75.  
  76. jelly_id : numpy.ndarray
  77. Identifier for the two creatures:
  78. 0 for even-indexed points
  79. 1 for odd-indexed points.
  80. """
  81.  
  82. # -------------------------------------------------------------------------
  83. # Point index
  84. # -------------------------------------------------------------------------
  85. #
  86. # i = 0, 1, 2, ..., n_points - 1
  87. #
  88. # These are not x-coordinates. They are indices used to generate the point
  89. # cloud.
  90. i = np.arange(n_points)
  91.  
  92. # -------------------------------------------------------------------------
  93. # Static quantities derived from i
  94. # -------------------------------------------------------------------------
  95. #
  96. # k = 9 * cos(i / 61)
  97. k = 9 * np.cos(i / 61)
  98.  
  99. # e = i / 652 - 13
  100. e = i / 652 - 13
  101.  
  102. # d = mag(k, e)^2 / 89 + 1
  103. #
  104. # In p5.js:
  105. #
  106. # mag(k, e) = sqrt(k^2 + e^2)
  107. #
  108. # Therefore:
  109. #
  110. # mag(k, e)^2 = k^2 + e^2
  111. d = (k**2 + e**2) / 89 + 1
  112.  
  113. # m = i % 2 * 3
  114. #
  115. # This alternates between 0 and 3:
  116. #
  117. # i even -> m = 0
  118. # i odd -> m = 3
  119. #
  120. # These two values create the two intertwined jellyfish-like creatures.
  121. m = (i % 2) * 3
  122.  
  123. # Optional identifier for the two creatures.
  124. jelly_id = i % 2
  125.  
  126. # -------------------------------------------------------------------------
  127. # Time variable
  128. # -------------------------------------------------------------------------
  129. #
  130. # Original JavaScript:
  131. #
  132. # t += PI / 45
  133. #
  134. # Here:
  135. #
  136. # frame_id = 1 -> t = pi / 45
  137. # frame_id = 2 -> t = 2*pi / 45
  138. # etc.
  139. t = frame_id * np.pi / 45
  140.  
  141. # -------------------------------------------------------------------------
  142. # Main deformation term
  143. # -------------------------------------------------------------------------
  144. #
  145. # q = 79 - e/2*sin(k) +
  146. # k/d * (6 + 5*sin(sin(d*d + e/9 - t + m)))
  147. q = (
  148. 79
  149. - e / 2 * np.sin(k)
  150. + k / d * (
  151. 6 + 5 * np.sin(
  152. np.sin(d**2 + e / 9 - t + m)
  153. )
  154. )
  155. )
  156.  
  157. # -------------------------------------------------------------------------
  158. # Angular term
  159. # -------------------------------------------------------------------------
  160. #
  161. # c = d/1.9 + cos(t - d*3 + m)/11 - t/16 + m
  162. c = (
  163. d / 1.9
  164. + np.cos(t - d * 3 + m) / 11
  165. - t / 16
  166. + m
  167. )
  168.  
  169. # -------------------------------------------------------------------------
  170. # Final coordinates
  171. # -------------------------------------------------------------------------
  172. #
  173. # x = q * sin(c) + 200
  174. # y = (q + 40) * cos(c) + 200
  175. x = q * np.sin(c) + 200
  176. y = (q + 40) * np.cos(c) + 200
  177.  
  178. return x, y, jelly_id
  179.  
  180. # =============================================================================
  181. # 3. Compute one frame
  182. # =============================================================================
  183.  
  184. x, y, jelly_id = jellyfish_coordinates(
  185. frame_id=frame_id,
  186. n_points=n_points
  187. )
  188.  
  189. # =============================================================================
  190. # 4. Plot one static PNG
  191. # =============================================================================
  192.  
  193. fig_width_inches = render_width_px / dpi
  194. fig_height_inches = render_height_px / dpi
  195.  
  196. fig, ax = plt.subplots(
  197. figsize=(fig_width_inches, fig_height_inches),
  198. dpi=dpi
  199. )
  200.  
  201. fig.patch.set_facecolor(background_colour)
  202. ax.set_facecolor(background_colour)
  203.  
  204. ax.set_xlim(0, canvas_width)
  205. ax.set_ylim(0, canvas_height)
  206. ax.set_aspect("equal")
  207.  
  208. ax.set_xticks([])
  209. ax.set_yticks([])
  210.  
  211. for spine in ax.spines.values():
  212. spine.set_visible(False)
  213.  
  214. ax.margins(0)
  215. plt.subplots_adjust(left=0, right=1, top=1, bottom=0)
  216.  
  217. ax.scatter(
  218. x,
  219. y,
  220. s=point_size,
  221. c=point_colour,
  222. alpha=point_alpha,
  223. linewidths=0
  224. )
  225.  
  226. fig.savefig(
  227. output_file,
  228. dpi=dpi,
  229. facecolor=fig.get_facecolor(),
  230. bbox_inches=None,
  231. pad_inches=0
  232. )
  233.  
  234. plt.close(fig)
  235.  
  236. print(f"Saved: {output_file}")
  237.  
  238.  
Advertisement
Add Comment
Please, Sign In to add comment