public static bool IsInRange <T>(this T[,] array, Vector2i pos) { return(pos.x >= 0 && pos.y >= 0 && pos.x < array.GetLength(0) && pos.y < array.GetLength(1)); }
public static void Set <T>(this T[,] array, Vector2i pos, T newVal) { array[pos.x, pos.y] = newVal; }
public static T Get <T>(this T[,] array, Vector2i pos) { return(array[pos.x, pos.y]); }
/// <summary> /// For every output pixel in the given region that isn't set yet, /// this method recalculates that pixel's /// "ApplicableColorFrequencies" and "VisualizedColor" fields. /// </summary> public void RecalculatePixelChances(Vector2i startAreaInclusive, Vector2i endAreaExclusive) { var outputPosFilter = OutputPosFilter; var outputColorGetter = OutputColorGetter; foreach (Vector2i _pixelPos in new Vector2i.Iterator(startAreaInclusive, endAreaExclusive)) { Vector2i pixelPos = outputPosFilter(_pixelPos); //If the position is outside the output, or the pixel is already set, don't bother. if (!Output.IsInRange(pixelPos) || Output.Get(pixelPos).FinalValue.HasValue) { continue; } var pixel = Output.Get(pixelPos); //Find any colors that, if placed here, would cause a violation of the WFC constraint. HashSet <Color> badColors = new HashSet <Color>(); foreach (Color color in Input.ColorFrequencies.Keys) { //Assume that the color is placed. Func <Vector2i, Color?> newOutputColorGetter = (outputPos) => { Vector2i filteredOutputPos = outputPosFilter(outputPos); if (filteredOutputPos == pixelPos) { return(color); } else if (Output.IsInRange(filteredOutputPos)) { return(Output.Get(filteredOutputPos).FinalValue); } else { return(null); } }; //Test the constraint: any NxM pattern in the output // appears at least once in the input. Vector2i minNearbyPixelPos = pixelPos - Input.MaxPatternSize + 1, maxNearbyPixelPos = pixelPos; foreach (Vector2i nearbyAffectedPixelPos in new Vector2i.Iterator(minNearbyPixelPos, maxNearbyPixelPos + 1)) { if (!Input.Patterns.Any(pattern => pattern.DoesFit(nearbyAffectedPixelPos, newOutputColorGetter))) { badColors.Add(color); break; } } } //Check which patterns can be applied at which positions around this pixel. pixel.ApplicableColorFrequencies.Clear(); foreach (Pattern pattern in Input.Patterns) { Vector2i patternSize = pattern.Values.SizeXY(); var patternPoses = new Vector2i.Iterator(pixelPos - patternSize + 1, pixelPos + 1); foreach (Vector2i patternMinCornerPos in patternPoses) { //See if the pattern can be placed here // (and the color this pixel would become isn't blacklisted). Vector2i pixelPatternPos = pixelPos - patternMinCornerPos; Color pixelPatternColor = pattern[pixelPatternPos]; if (!badColors.Contains(pixelPatternColor) && pattern.DoesFit(patternMinCornerPos, outputColorGetter)) { if (!pixel.ApplicableColorFrequencies.ContainsKey(pixelPatternColor)) { pixel.ApplicableColorFrequencies.Add(pixelPatternColor, 0); } pixel.ApplicableColorFrequencies[pixelPatternColor] += pattern.Frequency; } } } //Now update the visualized color of that pixel. //To do that, average together every color the pixel COULD be. if (!UpdateVisualizationAfterIteration) { continue; } float sumR = 0.0f, sumG = 0.0f, sumB = 0.0f; foreach (var colorAndFrequency in pixel.ApplicableColorFrequencies) { sumR += colorAndFrequency.Key.R * colorAndFrequency.Value; sumG += colorAndFrequency.Key.G * colorAndFrequency.Value; sumB += colorAndFrequency.Key.B * colorAndFrequency.Value; } float invN = 1.0f / pixel.ApplicableColorFrequencies.Values.Sum(u => (float)u); pixel.VisualizedValue = Color.FromArgb((byte)Math.Max(0, Math.Min(255, (int)(sumR * invN))), (byte)Math.Max(0, Math.Min(255, (int)(sumG * invN))), (byte)Math.Max(0, Math.Min(255, (int)(sumB * invN)))); } }
public bool IsInside(Vector2i inputPos) { return(inputPos.x >= 0 && inputPos.y >= 0 && inputPos.x < Size.x && inputPos.y < Size.y); }
public Input(Color[,] _data, Vector2i patternSize, bool periodicX, bool periodicY, bool useRotations, bool useReflections) { PeriodicX = periodicX; PeriodicY = periodicY; OriginalPatternSize = patternSize; //Copy the color data. data = new Color[_data.GetLength(0), _data.GetLength(1)]; foreach (Vector2i pos in data.AllIndices()) { this[pos] = _data.Get(pos); } //Compute the average color. float sumR = 0.0f, sumG = 0.0f, sumB = 0.0f; foreach (Vector2i pos in data.AllIndices()) { Color color = data.Get(pos); sumR += color.R; sumG += color.G; sumB += color.B; } float invN = 1.0f / (data.SizeX() * data.SizeY()); AverageColor = Color.FromRgb((byte)Math.Max(0, Math.Min(255, (int)(sumR * invN))), (byte)Math.Max(0, Math.Min(255, (int)(sumG * invN))), (byte)Math.Max(0, Math.Min(255, (int)(sumB * invN)))); //Get the list of transformations we'll be using on the input data. List <Transforms> transformations = new List <Transforms>() { Transforms.None }; if (useReflections) { transformations.Add(Transforms.MirrorX); transformations.Add(Transforms.MirrorY); } //The use of rotations also determines our maximum pattern size along each axis. if (useRotations) { transformations.Add(Transforms.Rotate90CW); transformations.Add(Transforms.Rotate270CW); transformations.Add(Transforms.Rotate180); int maxPatternSize = Math.Max(OriginalPatternSize.x, OriginalPatternSize.y); MaxPatternSize = new Vector2i(maxPatternSize, maxPatternSize); } else { MaxPatternSize = OriginalPatternSize; } //For every pixel in the input, create one pattern starting from that pixel // for each type of transformation. Patterns = new List <Pattern>(Size.x * Size.y * transformations.Count); //If the input is periodic, we can make our patterns all the way // to the max edge of the input. //Otherwise, we have to back off the max edge a bit. Vector2i maxPatternMinCorner = Size - 1; if (!PeriodicX) { maxPatternMinCorner.x -= patternSize.x; } if (!PeriodicY) { maxPatternMinCorner.y -= patternSize.y; } foreach (Vector2i patternMinCornerPos in new Vector2i.Iterator(maxPatternMinCorner + 2)) { foreach (var transform in transformations) { Patterns.Add(new Pattern(this, patternMinCornerPos, patternSize, transform)); } } //Remove identical patterns, using hashes to compare them quickly. List <int> patternHashes = Patterns.Select(p => p.GetHashCode()).ToList(); for (int i = 0; i < Patterns.Count; ++i) { var basePattern = Patterns[i]; for (int j = i + 1; j < Patterns.Count; ++j) { var patternToCheck = Patterns[j]; //If the patterns match, remove the second one. if (patternHashes[i] == patternHashes[j] && basePattern.Equals(patternToCheck)) { //Increment the frequency of the original pattern. Patterns[i] = new Pattern(Patterns[i].Values, Patterns[i].Frequency + 1); Patterns.RemoveAt(j); patternHashes.RemoveAt(j); j -= 1; } } } //Compute the color frequencies. ColorFrequencies = new Dictionary <Color, uint>(); foreach (Pattern pattern in Patterns) { foreach (Vector2i patternPos in pattern.Values.AllIndices()) { Color patternColor = pattern.Values.Get(patternPos); if (!ColorFrequencies.ContainsKey(patternColor)) { ColorFrequencies.Add(patternColor, 0); } ColorFrequencies[patternColor] += pattern.Frequency; } } }