package com.hypixel.hytale.server.worldgen.zoom;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import javax.annotation.Nonnull;
public class PixelDistanceProvider {
private static final int TABLE_SIZE = 8;
@Nonnull
protected final PixelProvider image;
protected final int width;
protected final int height;
protected final int cellsX;
protected final int cellsY;
@Nonnull
protected final IPixelSet[] table;
@Nonnull
protected final IntSet pixels;
public PixelDistanceProvider(@Nonnull PixelProvider image) {
this.image = image;
this.width = image.getWidth();
this.height = image.getHeight();
this.cellsX = image.getWidth() / 8;
this.cellsY = image.getHeight() / 8;
this.table = new IPixelSet[this.cellsX * this.cellsY];
this.pixels = new IntOpenHashSet();
this.prepareSegmentTable();
}
@Nonnull
public IntSet getColors() {
return this.pixels;
}
public double distanceSqToDifferentPixel(double ox, double oy, int px, int py) {
px = this.clampX(px);
py = this.clampY(py);
int color = this.image.getPixel(px, py);
int cellX = px / 8;
int cellY = py / 8;
double distance = 1.0 / 0.0;
int minX = Math.max(cellX - 1, 0);
int maxX = Math.min(cellX + 2, this.cellsX);
int minY = Math.max(cellY - 1, 0);
int maxY = Math.min(cellY + 2, this.cellsY);
for(int ix = minX; ix < maxX; ++ix) {
for(int iy = minY; iy < maxY; ++iy) {
double dist = this.distanceSqToDiffInSeq(ox, oy, color, ix, iy);
if (dist < distance) {
distance = dist;
}
}
}
return distance;
}
protected double distanceSqToDiffInSeq(double ox, double oy, int pixel, int cellX, int cellY) {
double distSq = 1.0 / 0.0;
if (this.hasDifferentPixel(cellX, cellY, pixel)) {
int offsetX = cellX * 8;
int offsetY = cellY * 8;
for(int ix = 0; ix < 8; ++ix) {
int px = ix + offsetX;
for(int iy = 0; iy < 8; ++iy) {
int py = iy + offsetY;
if (pixel != this.image.getPixel(px, py)) {
double dist = distanceSqToPixel(ox, oy, px, py);
if (dist < distSq) {
distSq = dist;
}
}
}
}
}
return distSq;
}
protected boolean hasDifferentPixel(int cellX, int cellY, int pixel) {
IPixelSet pixelSet = this.table[this.cellIndex(cellX, cellY)];
return !pixelSet.contains(pixel) || pixelSet.size() > 1;
}
private void prepareSegmentTable() {
for(int cellX = 0; cellX < this.cellsX; ++cellX) {
for(int cellY = 0; cellY < this.cellsY; ++cellY) {
IntSet colors = new IntOpenHashSet();
int offsetX = cellX * 8;
int offsetY = cellY * 8;
for(int ix = 0; ix < 8; ++ix) {
for(int iy = 0; iy < 8; ++iy) {
int x = ix + offsetX;
int y = iy + offsetY;
if (x < this.width && y < this.height) {
colors.add(this.image.getPixel(x, y));
}
}
}
this.pixels.addAll(colors);
if (colors.size() == 1) {
this.table[this.cellIndex(cellX, cellY)] = new SinglePixelSet(colors.iterator().nextInt());
} else {
this.table[this.cellIndex(cellX, cellY)] = new MultiplePixelSet(colors);
}
}
}
}
protected int clampX(int x) {
if (x < 0) {
return 0;
} else {
return x >= this.width ? this.width - 1 : x;
}
}
protected int clampY(int y) {
if (y < 0) {
return 0;
} else {
return y >= this.height ? this.height - 1 : y;
}
}
protected int cellIndex(int cellX, int cellY) {
return cellX * this.cellsY + cellY;
}
private static double distanceSqToPixel(double ox, double oy, int px, int py) {
double dx = Math.max(Math.max((double)px - ox, ox - (double)px - 1.0), 0.0);
double dy = Math.max(Math.max((double)py - oy, oy - (double)py - 1.0), 0.0);
return dx * dx + dy * dy;
}
private static class SinglePixelSet implements IPixelSet {
private final int pixel;
SinglePixelSet(int pixel) {
this.pixel = pixel;
}
public boolean contains(int pixel) {
return this.pixel == pixel;
}
public int size() {
return 1;
}
@Nonnull
public String toString() {
return "SinglePixelSet{pixel=" + this.pixel + "}";
}
}
private static class MultiplePixelSet implements IPixelSet {
private final IntSet pixels;
MultiplePixelSet(IntSet pixels) {
this.pixels = pixels;
}
public boolean contains(int pixel) {
return this.pixels.contains(pixel);
}
public int size() {
return this.pixels.size();
}
@Nonnull
public String toString() {
return "MultiplePixelSet{pixels=" + String.valueOf(this.pixels) + "}";
}
}
private interface IPixelSet {
boolean contains(int var1);
int size();
}
}