1 /**
2     Holds the (mor or less) independet OpenSimplexNoise implementation - original source and implementation: https://gist.github.com/KdotJPG/b1270127455a94ac5d19 and http://uniblock.tumblr.com/
3 */
4 module dosimplex.osimplex2d;
5 
6 import dosimplex.util;
7 
8 /// "Config" enum for the noise generator
9 private enum : double {
10     STRETCH_CONSTANT_2D = -0.211324865405187,    ///(1/Math.sqrt(2+1)-1)/2;
11     SQUISH_CONSTANT_2D = 0.366025403784439,      ///(Math.sqrt(2+1)-1)/2;
12     NORM_CONSTANT_2D = 47.,
13 }
14 
15 
16 /**
17     2D OpenSimplexNoise implementation. 
18 */
19 @nogc @safe pure double osNoise2D(double x, double y, const ref short[256] perm)
20 {
21     //Place input coordinates onto grid.
22     double stretchOffset = (x + y) * STRETCH_CONSTANT_2D;
23     double xs = x + stretchOffset;
24     double ys = y + stretchOffset;
25 
26     //Floor to get grid coordinates of rhombus (stretched square) super-cell origin.
27     int xsb = fastFloor(xs);
28     int ysb = fastFloor(ys);
29 
30     //Skew out to get actual coordinates of rhombus origin. We'll need these later.
31     double squishOffset = (xsb + ysb) * SQUISH_CONSTANT_2D;
32     double xb = xsb + squishOffset;
33     double yb = ysb + squishOffset;
34 
35     //Compute grid coordinates relative to rhombus origin.
36     double xins = xs - xsb;
37     double yins = ys - ysb;
38 
39     //Sum those together to get a value that determines which region we're in.
40     double inSum = xins + yins;
41 
42     //Positions relative to origin point.
43     double dx0 = x - xb;
44     double dy0 = y - yb;
45 
46     //We'll be defining these inside the next block and using them afterwards.
47     double dx_ext, dy_ext;
48     int xsv_ext, ysv_ext;
49 
50     double value = 0;
51 
52     //Contribution (1,0)
53     double dx1 = dx0 - 1 - SQUISH_CONSTANT_2D;
54     double dy1 = dy0 - 0 - SQUISH_CONSTANT_2D;
55     double attn1 = 2 - dx1 * dx1 - dy1 * dy1;
56     if (attn1 > 0) {
57         attn1 *= attn1;
58         value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, dx1, dy1, perm);
59     }
60 
61     //Contribution (0,1)
62     double dx2 = dx0 - 0 - SQUISH_CONSTANT_2D;
63     double dy2 = dy0 - 1 - SQUISH_CONSTANT_2D;
64     double attn2 = 2 - dx2 * dx2 - dy2 * dy2;
65     if (attn2 > 0) {
66         attn2 *= attn2;
67         value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, dx2, dy2, perm);
68     }
69 
70     if (inSum <= 1) { //We're inside the triangle (2-Simplex) at (0,0)
71         double zins = 1 - inSum;
72         if (zins > xins || zins > yins) { //(0,0) is one of the closest two triangular vertices
73             if (xins > yins) {
74                 xsv_ext = xsb + 1;
75                 ysv_ext = ysb - 1;
76                 dx_ext = dx0 - 1;
77                 dy_ext = dy0 + 1;
78             } else {
79                 xsv_ext = xsb - 1;
80                 ysv_ext = ysb + 1;
81                 dx_ext = dx0 + 1;
82                 dy_ext = dy0 - 1;
83             }
84         } else { //(1,0) and (0,1) are the closest two vertices.
85             xsv_ext = xsb + 1;
86             ysv_ext = ysb + 1;
87             dx_ext = dx0 - 1 - 2 * SQUISH_CONSTANT_2D;
88             dy_ext = dy0 - 1 - 2 * SQUISH_CONSTANT_2D;
89         }
90     } else { //We're inside the triangle (2-Simplex) at (1,1)
91         double zins = 2 - inSum;
92         if (zins < xins || zins < yins) { //(0,0) is one of the closest two triangular vertices
93             if (xins > yins) {
94                 xsv_ext = xsb + 2;
95                 ysv_ext = ysb + 0;
96                 dx_ext = dx0 - 2 - 2 * SQUISH_CONSTANT_2D;
97                 dy_ext = dy0 + 0 - 2 * SQUISH_CONSTANT_2D;
98             } else {
99                 xsv_ext = xsb + 0;
100                 ysv_ext = ysb + 2;
101                 dx_ext = dx0 + 0 - 2 * SQUISH_CONSTANT_2D;
102                 dy_ext = dy0 - 2 - 2 * SQUISH_CONSTANT_2D;
103             }
104         } else { //(1,0) and (0,1) are the closest two vertices.
105             dx_ext = dx0;
106             dy_ext = dy0;
107             xsv_ext = xsb;
108             ysv_ext = ysb;
109         }
110         xsb += 1;
111         ysb += 1;
112         dx0 = dx0 - 1 - 2 * SQUISH_CONSTANT_2D;
113         dy0 = dy0 - 1 - 2 * SQUISH_CONSTANT_2D;
114     }
115 
116     //Contribution (0,0) or (1,1)
117     double attn0 = 2 - dx0 * dx0 - dy0 * dy0;
118     if (attn0 > 0) {
119         attn0 *= attn0;
120         value += attn0 * attn0 * extrapolate(xsb, ysb, dx0, dy0, perm);
121     }
122 
123     //Extra Vertex
124     double attn_ext = 2 - dx_ext * dx_ext - dy_ext * dy_ext;
125     if (attn_ext > 0) {
126         attn_ext *= attn_ext;
127         value += attn_ext * attn_ext * extrapolate(xsv_ext, ysv_ext, dx_ext, dy_ext, perm);
128     }
129 
130     return value / NORM_CONSTANT_2D;
131 }
132 
133 /**
134     extrapolates for 2D
135 */
136 private @nogc @safe pure double extrapolate(int xsb, int ysb, double dx, double dy, const ref short[256] perm)
137 {
138     int index = perm[(perm[xsb & 0xFF] + ysb) & 0xFF] & 0x0E;
139     return GRADIENTS.GRADIENTS_2D[index] * dx + GRADIENTS.GRADIENTS_2D[index + 1] * dy;
140 }