Ejemplo n.º 1
0
        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);
        }