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;
}
}