/// <summary>
        /// Readies a biome map for creation and pulls heightmap
        /// information from the passed <see cref="Tile"/>
        /// </summary>
        /// <param name="tile">Tile to refernce heightmap from</param>
        /// <param name="resolution">Resolution of the map (must be lower
        /// than or equal to heightmap resolution)</param>
        public BiomeMap(Tile tile, int resolution)
        {
            _tile                = tile;
            _resolution          = resolution;
            _heightmap           = _tile.MeshManager.Heightmap;
            _heightmapResolution = _tile.MeshManager.HeightmapResolution;

            Map      = new BiomeData[resolution, resolution];
            BiomeSet = new List <BiomeData>();
        }
        /// <summary>
        /// Gets the bottom-most biome that is active within the list of
        /// biomes stored in <see cref="TerraConfig"/>.
        /// </summary>
        /// <param name="x">X location in heightmap</param>
        /// <param name="z">Z location in heightmap</param>
        public BiomeData GetBiomeAt(int x, int z)
        {
            BiomeData bottomMost = null;

            var tm = Config.TemperatureMapData;
            var mm = Config.MoistureMapData;

            //TODO Add angle constraint
            foreach (BiomeData b in Config.BiomesData)
            {
                if (b.IsTemperatureConstrained && !tm.HasGenerator())
                {
                    continue;
                }
                if (b.IsMoistureConstrained && !mm.HasGenerator())
                {
                    continue;
                }

                //Calculate x and z offsets for checking heightmap
                int offset = _resolution / _heightmapResolution;
                x *= offset;
                z *= offset;

                //Calculate height and world x/z positions
                var height = _heightmap[x, z];
                var local  = TileMesh.PositionToLocal(x, z, _resolution);
                var world  = TileMesh.LocalToWorld(_tile == null ? new GridPosition() : _tile.GridPosition, local.x, local.y);
                var wx     = world.x;
                var wz     = world.y;

                var temp     = tm.GetValue(wx, wz);
                var moisture = mm.GetValue(wx, wz);

                //If no constraints continue
                if (!b.IsHeightConstrained && !b.IsTemperatureConstrained && !b.IsMoistureConstrained)
                {
                    bottomMost = b;
                    continue;
                }

                //Which map constraints fit the passed value
                bool passHeight   = b.IsHeightConstrained && b.HeightConstraint.Fits(height);
                bool passTemp     = b.IsTemperatureConstrained && b.TemperatureConstraint.Fits(temp);
                bool passMoisture = b.IsMoistureConstrained && b.MoistureConstraint.Fits(moisture);

                if (passHeight && passTemp && passMoisture)
                {
                    bottomMost = b;
                }
            }

            return(bottomMost);
        }
        public void CreateMap()
        {
            WeightedMap      = new KeyValuePair <BiomeData[], float[]> [_resolution, _resolution];
            WeightedBiomeSet = new List <BiomeData>();

            for (int x = 0; x < _resolution; x++)
            {
                for (int z = 0; z < _resolution; z++)
                {
                    List <BiomeData> biomes  = new List <BiomeData>();
                    List <float>     weights = new List <float>();

                    //Collect weight of every biome at this coordinate
                    for (var i = 0; i < Config.BiomesData.Count; i++)
                    {
                        BiomeData b      = Config.BiomesData[i];
                        float     weight = GetWeight(b, x, z);

                        if (weight > 0.0001f)
                        {
                            biomes.Add(b);
                            weights.Add(weight);

                            if (i != 0)
                            {
                                weights[i - 1] = 1 - weight;
                                weights[i]     = weight;
                            }
                        }
                    }

                    //Normalize weights
                    float sum = weights.Sum();
                    for (var i = 0; i < weights.Count; i++)
                    {
                        weights[i] /= sum;
                    }

                    WeightedMap[x, z] = new KeyValuePair <BiomeData[], float[]>(biomes.ToArray(), weights.ToArray());

                    //Add to biome set
                    biomes.ForEach(b => {
                        if (!WeightedBiomeSet.Exists(existing => ReferenceEquals(existing, b)))
                        {
                            WeightedBiomeSet.Add(b);
                        }
                    });
                }
            }
        }
        public void CreateMap()
        {
            for (int x = 0; x < _resolution; x++)
            {
                for (int z = 0; z < _resolution; z++)
                {
                    //Get biome at this coordinate
                    BiomeData biome = GetBiomeAt(x, z);

                    Map[x, z] = biome;

                    //Add to biome set
                    BiomeSet.ForEach(b => {
                        if (!BiomeSet.Exists(existing => ReferenceEquals(existing, b)))
                        {
                            BiomeSet.Add(b);
                        }
                    });
                }
            }
        }
        /// <summary>
        /// Calculates the "weight", value between 0 and 1 which represents
        /// how much a biome appears. The <see cref="Tile"/> that was used
        /// when constructing must have a heightmap.
        /// </summary>
        /// <param name="biome">Biome to check</param>
        /// <param name="x">X location in heightmap</param>
        /// <param name="z">Z location in heightmap</param>
        public float GetWeight(BiomeData biome, int x, int z)
        {
            BiomeData b = biome;

            var tm = Config.TemperatureMapData;
            var mm = Config.MoistureMapData;

            if (b.IsTemperatureConstrained && !tm.HasGenerator())
            {
                return(0);
            }
            if (b.IsMoistureConstrained && !mm.HasGenerator())
            {
                return(0);
            }

            //Calculate x and z offsets for checking heightmap
            int offset = _resolution / _heightmapResolution;

            x *= offset;
            z *= offset;

            //Calculate height and world x/z positions
            var local = TileMesh.PositionToLocal(x, z, _resolution);
            var world = TileMesh.LocalToWorld(_tile == null ? new GridPosition() : _tile.GridPosition, local.x, local.y);
            var wx    = world.x;
            var wz    = world.y;

            //Establish and clamp sampled values between 1 & 0
            var height   = _heightmap[x, z];
            var temp     = tm.GetValue(wx, wz, tm.Spread);
            var moisture = mm.GetValue(wx, wz, mm.Spread);

            height   = Mathf.Clamp01(height);
            temp     = Mathf.Clamp01(temp);
            moisture = Mathf.Clamp01(moisture);

            //If no constraints return 1f
            if (!b.IsHeightConstrained && !b.IsTemperatureConstrained && !b.IsMoistureConstrained)
            {
                return(1f);
            }

            //Which map constraints fit the passed value                                     //todo figure this out v
            //bool passHeight = b.IsHeightConstrained && (b.HeightConstraint.Fits(height) || b.HeightConstraint.FitsMinMax(height));
            //bool passTemp = b.IsTemperatureConstrained && (b.TemperatureConstraint.Fits(temp) || b.TemperatureConstraint.FitsMinMax(temp));
            //bool passMoisture = b.IsMoistureConstrained && (b.MoistureConstraint.Fits(moisture) || b.MoistureConstraint.FitsMinMax(moisture));
            bool passHeight   = b.IsHeightConstrained && (b.HeightConstraint.Fits(height));
            bool passTemp     = b.IsTemperatureConstrained && (b.TemperatureConstraint.Fits(temp));
            bool passMoisture = b.IsMoistureConstrained && (b.MoistureConstraint.Fits(moisture));

            float blend = Config.Generator.BiomeBlendAmount;

            //Confirm constraint requirements
            int passAmt = new[] { passHeight, passTemp, passMoisture }.Count(pass => pass);
            int passMax = new[] { b.IsHeightConstrained, b.IsTemperatureConstrained, b.IsMoistureConstrained }.Count(pass => pass);

            //Not all constraints were passed, return 0f
            if (passAmt < passMax && b.MixMethod == BiomeData.ConstraintMixMethod.AND)
            {
                return(0f);
            }

            var maxWeight = new[] {
                new { value = height, pass = passHeight, constraint = b.HeightConstraint },
                new { value = temp, pass = passTemp, constraint = b.TemperatureConstraint },
                new { value = moisture, pass = passMoisture, constraint = b.MoistureConstraint }
            }
            .Where(type => type.pass)
            .Aggregate((agg, next) => agg.value > next.value ? next : agg);

            return(maxWeight.constraint.Weight(maxWeight.value, blend, Config.Generator.BiomeFalloff));

            //Calculate weight given to each map
//			float weight = 0;
//			if (passHeight) {
//				weight += b.HeightConstraint.Weight(height, blend);
//			}
//			if (passTemp) {
//				weight += b.TemperatureConstraint.Weight(temp, blend);
//			}
//			if (passMoisture) {
//				weight += b.MoistureConstraint.Weight(moisture, blend);
//			}
//
//			return weight / passAmt;
        }