Skip to content

Instantly share code, notes, and snippets.

@IllusionTheDev
Created May 16, 2023 15:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save IllusionTheDev/b730cdbeba8d022ba83262cc817371ad to your computer and use it in GitHub Desktop.
Save IllusionTheDev/b730cdbeba8d022ba83262cc817371ad to your computer and use it in GitHub Desktop.
Minecraft map color matching
import java.awt.*;
import java.util.Comparator;
/**
* The basic euclidean comparator is a simple comparator that compares two colors
* based on their euclidean distance.
*
* @author Illusion
*/
public class BasicEuclideanComparator implements Comparator<Color> {
@Override
public int compare(Color color, Color t1) {
int r1 = color.getRed();
int g1 = color.getGreen();
int b1 = color.getBlue();
int r2 = t1.getRed();
int g2 = t1.getGreen();
int b2 = t1.getBlue();
return (int) Math.sqrt(Math.pow(r1 - r2, 2) + Math.pow(g1 - g2, 2) + Math.pow(b1 - b2, 2));
}
}
import java.awt.*;
import java.util.Comparator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* The color matcher class is used to match a color to a Minecraft map color.
* You can define a custom comparator to use for matching colors, which can be tuned to your liking.
* The default comparator is the {@link BasicEuclideanComparator}.
* Another option is the {@link WeightedEuclideanComparator}, which produces brighter yellows and oranges.
* <p>
* These colors are defined up to 1.18.2, and it is expected that you will update this class when a new Minecraft version with new map colors is released.
*
* @author Illusion
*/
public class ColorMatcher {
private static final Color[] MAP_COLORS;
private static final Comparator<Color> COMPARATOR = new BasicEuclideanComparator();
private static final Map<Color, Integer> matchedColors = new ConcurrentHashMap<>();
static {
final Color[] baseMapColors = new Color[]
{
new Color(0, 0, 0, 0), // transparent
new Color(127, 178, 56), // grass
new Color(247, 233, 163), // sand
new Color(199, 199, 199), // wool
new Color(255, 0, 0), // fire
new Color(160, 160, 255), // ice
new Color(167, 167, 167), // metal
new Color(0, 124, 0), // Plant
new Color(255, 255, 255), // Snow
new Color(164, 168, 184), // Clay
new Color(151, 109, 77), // Dirt
new Color(112, 112, 112), // Stone
new Color(64, 64, 255), // Water
new Color(143, 119, 72), // Wood
//new 1.7 colors (13w42a/13w42b)
new Color(255, 252, 245), // Quartz
new Color(216, 127, 51), // COLOR_ORANGE
new Color(178, 76, 216), // COLOR_MAGENTA
new Color(102, 153, 216), // COLOR_LIGHT_BLUE
new Color(229, 229, 51), // COLOR_YELLOW
new Color(127, 204, 25), // COLOR_LIGHT_GREEN
new Color(242, 127, 165), // COLOR_PINK
new Color(76, 76, 76), // COLOR_GREY
new Color(153, 153, 153), // COLOR_LIGHT_GREY
new Color(76, 127, 153), // COLOR_CYAN
new Color(127, 63, 178), // COLOR_PURPLE
new Color(51, 76, 178), // COLOR_BLUE
new Color(102, 76, 51), // COLOR_BROWN
new Color(102, 127, 51), // COLOR_GREEN
new Color(153, 51, 51), // COLOR_RED
new Color(25, 25, 25), // COLOR_BLACK
new Color(250, 238, 77), // GOLD
new Color(92, 219, 213), // Diamond
new Color(74, 128, 255), // lapis
new Color(0, 217, 58), // emerald
new Color(129, 86, 49), // podzol
new Color(112, 2, 0), // NETHER
//new 1.8 colors
new Color(209, 177, 161), // TERRACOTTA_WHITE
new Color(159, 82, 36), // TERRACOTTA_ORANGE
new Color(149, 87, 108), // terracotta magenta
new Color(112, 108, 138), // terracotta_light_blue
new Color(186, 133, 36), // terracotta_yellow
new Color(103, 117, 53), // terracotta_light_green
new Color(160, 77, 78), // terracotta_pink
new Color(57, 41, 35), // terracotta_grey
new Color(135, 107, 98), // terracotta_light_grey
new Color(87, 92, 92), // terracotta_cyan
new Color(122, 73, 88), // terracotta_purple
new Color(76, 62, 92), // terracotta_blue
new Color(76, 50, 35), // terracotta_brown
new Color(76, 50, 35), // terracotta_green
new Color(142, 60, 46), // terracotta_red
new Color(37, 22, 16), // terracotta_black
//1.16
new Color(189, 48, 49), // CRIMson_nylium
new Color(148, 63, 97), // crimson_stem
new Color(92, 25, 29), // crimson_hyphae
new Color(22, 126, 134), // warped_nylium
new Color(58, 142, 140), // warped_stem
new Color(86, 44, 62), // warped_hyphae
new Color(20, 180, 133), // warped_wart_block
// cliffs update (1.18 / 1.19)
new Color(100, 100, 100), // deepslate
new Color(216, 175, 147), // raw iron
new Color(127, 167, 150), // glow lichen
};
MAP_COLORS = new Color[baseMapColors.length * 4];
for (int index = 0; index < baseMapColors.length; ++index) {
Color baseColor = baseMapColors[index];
MAP_COLORS[index * 4] = new Color(
(int) (baseColor.getRed() * 180.0 / 255.0 + 0.5),
(int) (baseColor.getGreen() * 180.0 / 255.0 + 0.5),
(int) (baseColor.getBlue() * 180.0 / 255.0 + 0.5),
baseColor.getAlpha()
);
MAP_COLORS[index * 4 + 1] = new Color(
(int) (baseColor.getRed() * 220.0 / 255.0 + 0.5),
(int) (baseColor.getGreen() * 220.0 / 255.0 + 0.5),
(int) (baseColor.getBlue() * 220.0 / 255.0 + 0.5),
baseColor.getAlpha()
);
MAP_COLORS[index * 4 + 2] = baseColor;
MAP_COLORS[index * 4 + 3] = new Color(
(int) (baseColor.getRed() * 135.0 / 255.0 + 0.5),
(int) (baseColor.getGreen() * 135.0 / 255.0 + 0.5),
(int) (baseColor.getBlue() * 135.0 / 255.0 + 0.5),
baseColor.getAlpha()
);
}
}
/**
* Returns the closest color index (in the map colors palette) for the given color.
* Results are cached.
*
* @param color the color to match
* @return the closest color index
*/
public static byte getColor(Color color) {
if (color.getAlpha() == 0) {
return 0;
}
byte cached = matchedColors.getOrDefault(color, -1).byteValue();
if (cached != -1) {
return cached;
}
int best = 0;
int bestDistance = Integer.MAX_VALUE;
for (int i = 0; i < MAP_COLORS.length; ++i) {
Color mappedColor = MAP_COLORS[i];
if (mappedColor.getAlpha() == 0) {
continue; // skip transparent colors
}
int distance = COMPARATOR.compare(color, mappedColor);
if (distance < bestDistance) {
bestDistance = distance;
best = i;
}
}
matchedColors.put(color, best);
return (byte) best;
}
/**
* Returns the color for the given index.
*
* @param original the original color
* @return the closest color available in the map colors palette
*/
public static Color getClosestColor(Color original) {
if (original.getAlpha() == 0) {
return new Color(0, 0, 0, 0);
}
int best = 0;
int bestDistance = Integer.MAX_VALUE;
for (int i = 0; i < MAP_COLORS.length; ++i) {
Color mappedColor = MAP_COLORS[i];
if (mappedColor.getAlpha() == 0) {
continue; // skip transparent colors
}
int distance = COMPARATOR.compare(original, mappedColor);
if (distance < bestDistance) {
bestDistance = distance;
best = i;
}
}
return MAP_COLORS[best];
}
}
import java.awt.*;
import java.util.Comparator;
/**
* The weighted euclidean comparator is a comparator that compares two colors
* based on their euclidean distance, but with a weighted variance.
*
* @author Illusion
*/
public class WeightedEuclideanComparator implements Comparator<Color> {
@Override
public int compare(Color color, Color t1) {
int r1 = color.getRed();
int g1 = color.getGreen();
int b1 = color.getBlue();
int r2 = t1.getRed();
int g2 = t1.getGreen();
int b2 = t1.getBlue();
int deltaR = r1 - r2;
int deltaG = g1 - g2;
int deltaB = b1 - b2;
int redVariance = (int) (1 / 2f * (r1 + r2));
if (redVariance < 128) {
return (int) (Math.sqrt(
Math.pow(2 * deltaR, 2) + Math.pow(4 * deltaG, 2) + Math.pow(3 * deltaB, 2)));
}
return (int) (Math.sqrt(
Math.pow(3 * deltaR, 2) + Math.pow(4 * deltaG, 2) + Math.pow(2 * deltaB, 2)));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment