using Terminal.Gui; using Color = Terminal.Gui.Color; /// /// Simple fast palette building algorithm which uses the frequency that a color is seen /// to determine whether it will appear in the final palette. Includes a threshold where /// by colors will be considered 'the same'. This reduces the chance of under represented /// colors being missed completely. /// public class PopularityPaletteWithThreshold : IPaletteBuilder { private readonly IColorDistance _colorDistance; private readonly double _mergeThreshold; /// /// Creates a new instance with the given color grouping parameters. /// /// Determines which different colors can be considered the same. /// Threshold for merging two colors together. public PopularityPaletteWithThreshold (IColorDistance colorDistance, double mergeThreshold) { _colorDistance = colorDistance; _mergeThreshold = mergeThreshold; // Set the threshold for merging similar colors } /// public List BuildPalette (List colors, int maxColors) { if (colors == null || colors.Count == 0 || maxColors <= 0) { return new (); } // Step 1: Build the histogram of colors (count occurrences) Dictionary colorHistogram = new (); foreach (Color color in colors) { if (colorHistogram.ContainsKey (color)) { colorHistogram [color]++; } else { colorHistogram [color] = 1; } } // If we already have fewer or equal colors than the limit, no need to merge if (colorHistogram.Count <= maxColors) { return colorHistogram.Keys.ToList (); } // Step 2: Merge similar colors using the color distance threshold Dictionary mergedHistogram = MergeSimilarColors (colorHistogram, maxColors); // Step 3: Sort the histogram by frequency (most frequent colors first) List sortedColors = mergedHistogram.OrderByDescending (c => c.Value) .Take (maxColors) // Keep only the top `maxColors` colors .Select (c => c.Key) .ToList (); return sortedColors; } /// /// Merge colors in the histogram if they are within the threshold distance /// /// /// /// private Dictionary MergeSimilarColors (Dictionary colorHistogram, int maxColors) { Dictionary mergedHistogram = new (); foreach (KeyValuePair entry in colorHistogram) { Color currentColor = entry.Key; var merged = false; // Try to merge the current color with an existing entry in the merged histogram foreach (Color mergedEntry in mergedHistogram.Keys.ToList ()) { double distance = _colorDistance.CalculateDistance (currentColor, mergedEntry); // If the colors are similar enough (within the threshold), merge them if (distance <= _mergeThreshold) { mergedHistogram [mergedEntry] += entry.Value; // Add the color frequency to the existing one merged = true; break; } } // If no similar color is found, add the current color as a new entry if (!merged) { mergedHistogram [currentColor] = entry.Value; } // Early exit if we've reduced the colors to the maxColors limit if (mergedHistogram.Count >= maxColors) { return mergedHistogram; } } return mergedHistogram; } }