simplex_algorithm_deconstruction.py 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. import numpy as np
  2. import matplotlib.pyplot as pl
  3. # Based on: https://gist.github.com/KdotJPG/b1270127455a94ac5d19
  4. import sys
  5. from ctypes import c_int64
  6. from math import floor as _floor
  7. if sys.version_info[0] < 3:
  8. def floor(num):
  9. return int(_floor(num))
  10. else:
  11. floor = _floor
  12. STRETCH_CONSTANT_2D = -0.211324865405187 # (1/Math.sqrt(2+1)-1)/2
  13. SQUISH_CONSTANT_2D = 0.366025403784439 # (Math.sqrt(2+1)-1)/2
  14. STRETCH_CONSTANT_3D = -1.0 / 6 # (1/Math.sqrt(3+1)-1)/3
  15. SQUISH_CONSTANT_3D = 1.0 / 3 # (Math.sqrt(3+1)-1)/3
  16. STRETCH_CONSTANT_4D = -0.138196601125011 # (1/Math.sqrt(4+1)-1)/4
  17. SQUISH_CONSTANT_4D = 0.309016994374947 # (Math.sqrt(4+1)-1)/4
  18. NORM_CONSTANT_2D = 47
  19. NORM_CONSTANT_3D = 103
  20. NORM_CONSTANT_4D = 30
  21. DEFAULT_SEED = 0
  22. # Gradients for 2D. They approximate the directions to the
  23. # vertices of an octagon from the center.
  24. GRADIENTS_2D = (
  25. 5, 2, 2, 5,
  26. -5, 2, -2, 5,
  27. 5, -2, 2, -5,
  28. -5, -2, -2, -5,
  29. )
  30. # Gradients for 3D. They approximate the directions to the
  31. # vertices of a rhombicuboctahedron from the center, skewed so
  32. # that the triangular and square facets can be inscribed inside
  33. # circles of the same radius.
  34. GRADIENTS_3D = (
  35. -11, 4, 4, -4, 11, 4, -4, 4, 11,
  36. 11, 4, 4, 4, 11, 4, 4, 4, 11,
  37. -11, -4, 4, -4, -11, 4, -4, -4, 11,
  38. 11, -4, 4, 4, -11, 4, 4, -4, 11,
  39. -11, 4, -4, -4, 11, -4, -4, 4, -11,
  40. 11, 4, -4, 4, 11, -4, 4, 4, -11,
  41. -11, -4, -4, -4, -11, -4, -4, -4, -11,
  42. 11, -4, -4, 4, -11, -4, 4, -4, -11,
  43. )
  44. # Gradients for 4D. They approximate the directions to the
  45. # vertices of a disprismatotesseractihexadecachoron from the center,
  46. # skewed so that the tetrahedral and cubic facets can be inscribed inside
  47. # spheres of the same radius.
  48. GRADIENTS_4D = (
  49. 3, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3,
  50. -3, 1, 1, 1, -1, 3, 1, 1, -1, 1, 3, 1, -1, 1, 1, 3,
  51. 3, -1, 1, 1, 1, -3, 1, 1, 1, -1, 3, 1, 1, -1, 1, 3,
  52. -3, -1, 1, 1, -1, -3, 1, 1, -1, -1, 3, 1, -1, -1, 1, 3,
  53. 3, 1, -1, 1, 1, 3, -1, 1, 1, 1, -3, 1, 1, 1, -1, 3,
  54. -3, 1, -1, 1, -1, 3, -1, 1, -1, 1, -3, 1, -1, 1, -1, 3,
  55. 3, -1, -1, 1, 1, -3, -1, 1, 1, -1, -3, 1, 1, -1, -1, 3,
  56. -3, -1, -1, 1, -1, -3, -1, 1, -1, -1, -3, 1, -1, -1, -1, 3,
  57. 3, 1, 1, -1, 1, 3, 1, -1, 1, 1, 3, -1, 1, 1, 1, -3,
  58. -3, 1, 1, -1, -1, 3, 1, -1, -1, 1, 3, -1, -1, 1, 1, -3,
  59. 3, -1, 1, -1, 1, -3, 1, -1, 1, -1, 3, -1, 1, -1, 1, -3,
  60. -3, -1, 1, -1, -1, -3, 1, -1, -1, -1, 3, -1, -1, -1, 1, -3,
  61. 3, 1, -1, -1, 1, 3, -1, -1, 1, 1, -3, -1, 1, 1, -1, -3,
  62. -3, 1, -1, -1, -1, 3, -1, -1, -1, 1, -3, -1, -1, 1, -1, -3,
  63. 3, -1, -1, -1, 1, -3, -1, -1, 1, -1, -3, -1, 1, -1, -1, -3,
  64. -3, -1, -1, -1, -1, -3, -1, -1, -1, -1, -3, -1, -1, -1, -1, -3,
  65. )
  66. def overflow(x):
  67. # Since normal python ints and longs can be quite humongous we have to use
  68. # this hack to make them be able to overflow
  69. return c_int64(x).value
  70. class OpenSimplex(object):
  71. """
  72. OpenSimplex n-dimensional gradient noise functions.
  73. """
  74. def __init__(self, seed=DEFAULT_SEED):
  75. """
  76. Initiate the class using a permutation array generated from a 64-bit seed number.
  77. """
  78. # Generates a proper permutation (i.e. doesn't merely perform N
  79. # successive pair swaps on a base array)
  80. perm = self._perm = [0] * 256 # Have to zero fill so we can properly loop over it later
  81. perm_grad_index_3D = self._perm_grad_index_3D = [0] * 256
  82. source = [i for i in range(0, 256)]
  83. seed = overflow(seed * 6364136223846793005 + 1442695040888963407)
  84. seed = overflow(seed * 6364136223846793005 + 1442695040888963407)
  85. seed = overflow(seed * 6364136223846793005 + 1442695040888963407)
  86. for i in range(255, -1, -1):
  87. seed = overflow(seed * 6364136223846793005 + 1442695040888963407)
  88. r = int((seed + 31) % (i + 1))
  89. if r < 0:
  90. r += i + 1
  91. perm[i] = source[r]
  92. perm_grad_index_3D[i] = int((perm[i] % (len(GRADIENTS_3D) / 3)) * 3)
  93. source[r] = source[i]
  94. def _extrapolate2d(self, xsb, ysb, dx, dy):
  95. perm = self._perm
  96. index = perm[(perm[xsb & 0xFF] + ysb) & 0xFF] & 0x0E
  97. g1, g2 = GRADIENTS_2D[index:index + 2]
  98. return g1 * dx + g2 * dy
  99. def _extrapolate3d(self, xsb, ysb, zsb, dx, dy, dz):
  100. perm = self._perm
  101. index = self._perm_grad_index_3D[
  102. (perm[(perm[xsb & 0xFF] + ysb) & 0xFF] + zsb) & 0xFF
  103. ]
  104. g1, g2, g3 = GRADIENTS_3D[index:index + 3]
  105. return g1 * dx + g2 * dy + g3 * dz
  106. def _extrapolate4d(self, xsb, ysb, zsb, wsb, dx, dy, dz, dw):
  107. perm = self._perm
  108. index = perm[(
  109. perm[(
  110. perm[(perm[xsb & 0xFF] + ysb) & 0xFF] + zsb
  111. ) & 0xFF] + wsb
  112. ) & 0xFF] & 0xFC
  113. g1, g2, g3, g4 = GRADIENTS_4D[index:index + 4]
  114. return g1 * dx + g2 * dy + g3 * dz + g4 * dw
  115. def noise2d(self, x, y):
  116. """
  117. Generate 2D OpenSimplex noise from X,Y coordinates.
  118. """
  119. # Place input coordinates onto grid.
  120. stretch_offset = (x + y) * STRETCH_CONSTANT_2D
  121. xs = x + stretch_offset
  122. ys = y + stretch_offset
  123. # Floor to get grid coordinates of rhombus (stretched square) super-cell origin.
  124. xsb = floor(xs)
  125. ysb = floor(ys)
  126. # Skew out to get actual coordinates of rhombus origin. We'll need these later.
  127. squish_offset = (xsb + ysb) * SQUISH_CONSTANT_2D
  128. xb = xsb + squish_offset
  129. yb = ysb + squish_offset
  130. # Compute grid coordinates relative to rhombus origin.
  131. xins = xs - xsb
  132. yins = ys - ysb
  133. # Sum those together to get a value that determines which region we're in.
  134. in_sum = xins + yins
  135. # Positions relative to origin point.
  136. dx0 = x - xb
  137. dy0 = y - yb
  138. value = 0
  139. # Contribution (1,0)
  140. dx1 = dx0 - 1 - SQUISH_CONSTANT_2D
  141. dy1 = dy0 - 0 - SQUISH_CONSTANT_2D
  142. attn1 = 2 - dx1 * dx1 - dy1 * dy1
  143. extrapolate = self._extrapolate2d
  144. if attn1 > 0:
  145. attn1 *= attn1
  146. value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, dx1, dy1)
  147. # Contribution (0,1)
  148. dx2 = dx0 - 0 - SQUISH_CONSTANT_2D
  149. dy2 = dy0 - 1 - SQUISH_CONSTANT_2D
  150. attn2 = 2 - dx2 * dx2 - dy2 * dy2
  151. if attn2 > 0:
  152. attn2 *= attn2
  153. value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, dx2, dy2)
  154. if in_sum <= 1: # We're inside the triangle (2-Simplex) at (0,0)
  155. zins = 1 - in_sum
  156. if zins > xins or zins > yins: # (0,0) is one of the closest two triangular vertices
  157. if xins > yins:
  158. xsv_ext = xsb + 1
  159. ysv_ext = ysb - 1
  160. dx_ext = dx0 - 1
  161. dy_ext = dy0 + 1
  162. else:
  163. xsv_ext = xsb - 1
  164. ysv_ext = ysb + 1
  165. dx_ext = dx0 + 1
  166. dy_ext = dy0 - 1
  167. else: # (1,0) and (0,1) are the closest two vertices.
  168. xsv_ext = xsb + 1
  169. ysv_ext = ysb + 1
  170. dx_ext = dx0 - 1 - 2 * SQUISH_CONSTANT_2D
  171. dy_ext = dy0 - 1 - 2 * SQUISH_CONSTANT_2D
  172. else: # We're inside the triangle (2-Simplex) at (1,1)
  173. zins = 2 - in_sum
  174. if zins < xins or zins < yins: # (0,0) is one of the closest two triangular vertices
  175. if xins > yins:
  176. xsv_ext = xsb + 2
  177. ysv_ext = ysb + 0
  178. dx_ext = dx0 - 2 - 2 * SQUISH_CONSTANT_2D
  179. dy_ext = dy0 + 0 - 2 * SQUISH_CONSTANT_2D
  180. else:
  181. xsv_ext = xsb + 0
  182. ysv_ext = ysb + 2
  183. dx_ext = dx0 + 0 - 2 * SQUISH_CONSTANT_2D
  184. dy_ext = dy0 - 2 - 2 * SQUISH_CONSTANT_2D
  185. else: # (1,0) and (0,1) are the closest two vertices.
  186. dx_ext = dx0
  187. dy_ext = dy0
  188. xsv_ext = xsb
  189. ysv_ext = ysb
  190. xsb += 1
  191. ysb += 1
  192. dx0 = dx0 - 1 - 2 * SQUISH_CONSTANT_2D
  193. dy0 = dy0 - 1 - 2 * SQUISH_CONSTANT_2D
  194. # Contribution (0,0) or (1,1)
  195. attn0 = 2 - dx0 * dx0 - dy0 * dy0
  196. if attn0 > 0:
  197. attn0 *= attn0
  198. value += attn0 * attn0 * extrapolate(xsb, ysb, dx0, dy0)
  199. # Extra Vertex
  200. attn_ext = 2 - dx_ext * dx_ext - dy_ext * dy_ext
  201. if attn_ext > 0:
  202. attn_ext *= attn_ext
  203. value += attn_ext * attn_ext * extrapolate(xsv_ext, ysv_ext, dx_ext, dy_ext)
  204. return value / NORM_CONSTANT_2D
  205. noise = OpenSimplex()
  206. nrow = 60
  207. size = 900
  208. scale = 200
  209. x = y = np.linspace(0, size, nrow)
  210. n = [[noise.noise2d(i/scale, j/scale) for j in y] for i in x]
  211. m = np.concatenate(n)
  212. sorted_idx = np.argsort(m)
  213. max_val = nrow * 2
  214. idx = len(m) // max_val
  215. for ii, val in enumerate(range(max_val)):
  216. m[sorted_idx[ii * idx:(ii + 1) * idx]] = val
  217. landscape = (m - nrow) / nrow
  218. fig, ax = pl.subplots(1, 1)
  219. xmin = np.min(x)
  220. xmax = np.max(x)
  221. dx = (xmax - xmin) / (x.shape[0] - 1)
  222. ymin = np.min(y)
  223. ymax = np.max(y)
  224. dy = (ymax - ymin) / (y.shape[0] - 1)
  225. X, Y = np.meshgrid(np.arange(xmin, xmax + 2 * dx, dx) - dx / 2., np.arange(ymin, ymax + 2 * dy, dy) - dy / 2.)
  226. c = ax.pcolor(X, Y, landscape.reshape(nrow, -1), vmin=-1, vmax=1, cmap='hsv')
  227. fig.colorbar(c, ax=ax, label="in/out-degree")
  228. # pl.matshow(landscape.reshape(nrow, -1), vmin=-1, vmax=1)
  229. # print(x)
  230. # pl.gca().set_xticklabels(x)
  231. # pl.gca().set_yticklabels(y)
  232. # pl.colorbar()
  233. print(1/(1+2*STRETCH_CONSTANT_2D))
  234. print((1/(1+2*STRETCH_CONSTANT_2D))/np.sqrt(3))
  235. pl.show()