public float CalculateNoiseValue(NoiseAlgorithm noiseAlgorithm, float i, float j, Terrain chunk, float[] args)
        {
            float noiseValue = 0.0f;

            switch (noiseData.type)
            {
            case eNoise.FRACTAL:
            {
                noiseValue = (float)(noiseAlgorithm.RidgedMultiFractal((seed.x + (float)i / (float)chunk.terrainData.heightmapHeight),
                                                                       (seed.y + (float)j / (float)chunk.terrainData.heightmapWidth),
                                                                       (int)args[0], args[1], args[2], args[3])) / noiseData.size;
                break;
            }

            case eNoise.FBM:
            {
                noiseValue = (float)(noiseAlgorithm.FractionalBrownianMotion((seed.x + (float)i / (float)chunk.terrainData.heightmapHeight),
                                                                             (seed.y + (float)j / (float)chunk.terrainData.heightmapWidth),
                                                                             (int)args[0], args[1], args[2], args[3])) / noiseData.size;
                break;
            }

            case eNoise.BILLOW:
            {
                noiseValue = (float)(noiseAlgorithm.Billow((seed.x + (float)i / (float)chunk.terrainData.heightmapHeight),
                                                           (seed.y + (float)j / (float)chunk.terrainData.heightmapWidth),
                                                           (int)args[0], args[1], args[2], args[3])) / noiseData.size;
                break;
            }
            }

            return(noiseValue);
        }
        // ###################################################################################################################
        // ###################################################################################################################
        // ###################################################################################################################

        public override void GenerateNoise(bool update)
        {
            // #### Adds base noise in whole terrain ####
            //generate(dir, type, update);

            NoiseAlgorithm noiseAlgorithm = new NoiseAlgorithm();

            int totalHeight = (_chunks[0, 0].chunkTerrain.terrainData.heightmapHeight - 1) * chunksY;
            int totalWidth  = (_chunks[0, 0].chunkTerrain.terrainData.heightmapWidth - 1) * chunksX;

            int x = 0, y = 0;
            int ix = 1, iy = 1;

            // Starting for first chunk grid ("00")
            // 01 11
            // 00 10
            Terrain chunk      = _chunks[x, y].chunkTerrain;
            Terrain chunk_init = chunk;

            float[,] heights      = chunk.terrainData.GetHeights(0, 0, chunk.terrainData.heightmapHeight, chunk.terrainData.heightmapWidth);
            float[,] heights_init = heights;

            int initH = (int)chunk.transform.position.z;
            int initW = (int)chunk.transform.position.x;

            int endH = initH + totalHeight;
            int endW = initW + totalWidth;

            int currentHeight = initH + (chunk.terrainData.heightmapHeight - 2);
            int currentWidth  = initW + (chunk.terrainData.heightmapWidth - 1);


            Vector2 center = new Vector2(totalWidth * 0.5f, totalHeight * 0.5f);

            float offset             = (chunksX == chunksY ? 0.85f : 1f);
            float MaxDistance2Center = Vector2.Distance(center, new Vector2(0, 0)) * offset;

            // List for store/apply heights optimally
            List <float[, ]> nextHeights = StoreNextHeights(y);


            NoiseLayer noiseLayer = NoiseManager.noiseLayers[NoiseManager.currLayer];

            // Set seed depending of seedIgnore
            noiseLayer.seed.x = (noiseLayer.seedIgnore ? (Random.value * 10f) : noiseLayer.seed.x);
            noiseLayer.seed.y = (noiseLayer.seedIgnore ? (Random.value * 10f) : noiseLayer.seed.y);


            // GetData en auxiliar variable, for don't erase noiseLayer data
            float[] args = noiseLayer.GetData();

            int acum = update ? 0 : 1;

            UnityEditor.Undo.RegisterCompleteObjectUndo(chunk.terrainData, "Noise");

            // Loop total height
            for (int i = initH; i <= endH; i++)
            {
                //Loop total width
                for (int j = initW; j < endW; j++)
                {
                    if (j == currentWidth - 1)
                    {
                        // Store to heights lists
                        nextHeights[x] = heights;

                        ix = 1;
                        x++;

                        if (x < chunksX)
                        {
                            // Get next x-chunk (the chunk "10") , and update limit of currentWidth (cause it have a new position)
                            // 01 11
                            // 00 10
                            chunk         = _chunks[x, y].chunkTerrain;
                            currentWidth += (chunk.terrainData.heightmapWidth - 1);
                            heights       = nextHeights[x];
                        }
                    }
                    else
                    {
                        float noiseValue = 0.0f;

                        // Do noise depending on type noise. See the function
                        noiseValue = noiseLayer.CalculateNoiseValue(noiseAlgorithm, i, j, chunk, args);

                        float distance2center = Vector2.Distance(center, new Vector2(j, i));

                        //float flattenGrade = (dir == "+" || dir == "-" ? 1f : 1f - (distance2center / MaxDistance2Center));
                        float flattenGrade = 1f - (distance2center / MaxDistance2Center);

                        if (!noiseLayer.islandMode)
                        {
                            flattenGrade = 1f;
                        }

                        float heighresult = noiseValue * flattenGrade;

                        // if we have "update" to TRUE then "acum" equals to ZERO,..
                        // so if (heights * acum = 0), then it doesn't accumulate value over previous heights
                        heights[iy, ix] = (heights[iy, ix] * acum) + heighresult;
                        ix++;
                    }
                }

                x  = 0;
                ix = 1;
                iy++;

                if (i == currentHeight - 1)
                {
                    // In our example. Apply first heights in chunks "00" and "10". (In the next chance in chunks "01" and "11"...)
                    ApplyStoreHeights(nextHeights, y);

                    iy = 1;
                    y++;

                    if (y < chunksY)
                    {
                        // Get next y-chunk (the chunk "01") , and update limit of currentHeight (cause it have a new position)
                        // 01 11
                        // 00 10
                        chunk          = _chunks[x, y].chunkTerrain;
                        currentHeight += (chunk.terrainData.heightmapHeight - 2);
                        chunk_init     = chunk;
                        heights_init   = chunk.terrainData.GetHeights(0, 0, chunk.terrainData.heightmapHeight, chunk.terrainData.heightmapWidth);
                        nextHeights    = StoreNextHeights(y);
                    }
                    else
                    {
                        // end algorithm ...
                        return;
                    }
                }
                else
                {
                    // Back to initial chunk (in our example: back to "01")
                    chunk = chunk_init;
                }

                heights      = heights_init;
                currentWidth = (int)chunk.transform.position.x + (chunk.terrainData.heightmapWidth - 1);
            }
        }