public override double GetValue(double x, double y) { double frequency = Frequency.GetValue(x, y); x *= frequency; y *= frequency; int xInt = (x > 0.0 ? (int)x : (int)x - 1); int yInt = (y > 0.0 ? (int)y : (int)y - 1); // Get mode/method: VoronoiMethod method = (VoronoiMethod)((int)Method.GetValue(x, y)); VoronoiDistance distance = (VoronoiDistance)((int)Distance.GetValue(x, y)); VoronoiDraw drawMode = (VoronoiDraw)((int)DrawMode.GetValue(x, y)); // Get the buffer: double[] buffer = DistanceBuffer; int bufferLength = buffer.Length; // Reset mins: for (int i = 0; i < bufferLength; i += 2) { // Up to max: buffer[i] = double.MaxValue; } // Inside each unit cube, there is a seed point at a random position. Go // through each of the nearby cubes until we find a cube with a seed point // that is closest to the specified position. for (int yCur = yInt - 2; yCur <= yInt + 2; yCur++) { for (int xCur = xInt - 2; xCur <= xInt + 2; xCur++) { // Calculate the position and distance to the seed point inside of // this unit cube. double xPos = xCur + ValueNoiseBasis.ValueNoise(xCur, yCur, Seed); double yPos = yCur + ValueNoiseBasis.ValueNoise(xCur, yCur, Seed + 1); double xDist = xPos - x; double yDist = yPos - y; double dist; // Sample and return at the successful point: int x0 = (xPos > 0.0 ? (int)xPos : (int)xPos - 1); int y0 = (yPos > 0.0 ? (int)yPos : (int)yPos - 1); // Min is.. double min = ((double)ValueNoiseBasis.ValueNoise(x0, y0) + 1.0) * 0.5; switch (distance) { default: case VoronoiDistance.Euclidean: dist = xDist * xDist + yDist * yDist; if (drawMode == VoronoiDraw.Normal) { dist += 1.0 - min; } break; case VoronoiDistance.Manhattan: if (yDist < 0) { yDist = -yDist; } if (xDist < 0) { xDist = -xDist; } dist = xDist + yDist; if (drawMode == VoronoiDraw.Normal) { dist += 1.0 - min; } dist /= 2.0; break; case VoronoiDistance.Chebyshev: if (yDist < 0) { yDist = -yDist; } if (xDist < 0) { xDist = -xDist; } if (yDist > xDist) { dist = yDist; } else { dist = xDist; } if (drawMode == VoronoiDraw.Normal) { if (dist < min) { dist = min; } } break; case VoronoiDistance.Minkowski: if (yDist < 0) { yDist = -yDist; } if (xDist < 0) { xDist = -xDist; } double minkowskiNumber = MinkowskiNumber.GetValue(x, y); dist = System.Math.Pow((System.Math.Pow(xDist, minkowskiNumber) + System.Math.Pow(yDist, minkowskiNumber)), 1.0 / minkowskiNumber); if (drawMode == VoronoiDraw.Normal) { if (dist < min) { dist = min; } } break; } // Note: The nearest is at [0]. for (int i = 0; i < bufferLength; i += 2) { // Up to max: if (dist < buffer[i]) { // This seed point is closer than the one at buffer[i/2]. // Push i onwards over by 2 places as the entry here // and all after it are now further away. int offset = i + 2; if (bufferLength != offset) { Array.Copy(buffer, i, buffer, offset, bufferLength - offset); } // Write this: buffer[i] = dist; buffer[i + 1] = min; // Stop there. break; } } } } // Buffer now contains n nodes. The nearest one is at the start of the buffer. double value; // Special case for euclidean - we used basic lengths for speed: if (distance == VoronoiDistance.Euclidean) { // Must compute the full distance. So, for each one.. for (int i = 0; i < bufferLength; i += 2) { // Get complete value: buffer[i] = (System.Math.Sqrt(buffer[i])) * Math.Sqrt3 - 1.0; } } // Next, compute the point and offset values. if (drawMode == VoronoiDraw.Solid) { switch (method) { default: case VoronoiMethod.F1: // Best distance was.. value = buffer[1]; break; case VoronoiMethod.F2: // 2nd best distance was.. value = buffer[3]; break; case VoronoiMethod.F3: // 3rd best distance was.. value = buffer[5]; break; case VoronoiMethod.F4: // 4th best distance was.. value = buffer[7]; break; case VoronoiMethod.F2minusF1: case VoronoiMethod.F3minusF2: case VoronoiMethod.F4minusF3: // fN - fNm1 (but inverted): value = buffer[bufferLength - 3] - buffer[bufferLength - 1] + 1.0; break; case VoronoiMethod.Average2: case VoronoiMethod.Average3: case VoronoiMethod.Average4: // Sum all of them together: value = 0.0; for (int i = 1; i < bufferLength; i += 2) { value += buffer[i]; } // Average is then.. value /= (double)bufferLength; break; case VoronoiMethod.TwoF3minusF2minusF1: // 2 * f3 - f2 - f1: value = 1.0 - ((2.0 * buffer[5]) - buffer[3] - buffer[1]); break; } } else { switch (method) { default: case VoronoiMethod.F1: // Best distance was.. value = buffer[0]; break; case VoronoiMethod.F2: // 2nd best distance was.. value = buffer[2]; break; case VoronoiMethod.F3: // 3rd best distance was.. value = buffer[4]; break; case VoronoiMethod.F4: // 4th best distance was.. value = buffer[6]; break; case VoronoiMethod.F2minusF1: case VoronoiMethod.F3minusF2: case VoronoiMethod.F4minusF3: // fN - fNm1 (but inverted): value = buffer[bufferLength - 4] - buffer[bufferLength - 2] + 1.0; break; case VoronoiMethod.Average2: case VoronoiMethod.Average3: case VoronoiMethod.Average4: // Sum all of them together: value = 0.0; for (int i = 0; i < bufferLength; i += 2) { value += buffer[i]; } // Average is then.. value /= (double)bufferLength; break; case VoronoiMethod.TwoF3minusF2minusF1: // 2 * f3 - f2 - f1: value = 1.0 - ((2.0 * buffer[4]) - buffer[2] - buffer[0]); break; } } // Return the calculated distance. if (distance == VoronoiDistance.Euclidean) { return((1.0 - value) / 2.0); } return(1.0 - value); }