All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.uci.qa.performancedriver.util.PaletteGenerator Maven / Gradle / Ivy

The newest version!
package edu.uci.qa.performancedriver.util;

import java.util.function.Function;

public final class PaletteGenerator {
  
  public static Integer[] generate(int colorCount) {
    return generate(colorCount, c -> {
      Color out = new Color();
      ColorConversions.RGBtoLCH(c, out);
      return out.getZ() >= 0 && out.getZ() <= 360 && out.getY() >= 30 && out.getY() <= 80
          && out.getX() >= 35 && out.getX() <= 80;
    });
  }
  
  public static Integer[] generate(int colorCount, Function checker) {
    // TODO: K-Means should be the default setting.
    return generate(colorCount, checker, true, 50, false, Type.DEFAULT);
  }
  
  public static Integer[] generate(int colorCount, Function checker,
      boolean forceMode, int quality, boolean ultraPrecision, Type distanceType) {
    if (colorCount <= 0) {
      colorCount = 8;
    }
    if (checker == null) {
      checker = c -> true;
    }
    if (quality <= 0) {
      quality = 50;
    }
    forceMode = true; // TODO: Implement k-means mode
    
    if (forceMode) {
      Double[][] colors = new Double[colorCount][3];
      
      for (int i = 0; i < colorCount; i++) {
        Double[] color = null;
        do {
          color = new Double[] {100 * Math.random(), 100 * (2 * Math.random() - 1),
              100 * (2 * Math.random() - 1)};
        } while (!checkLab(color, checker));
        colors[i] = color;
      }
      
      int repulsion = 100;
      int speed = 100;
      int steps = quality * 20;
      Vector[] vectors = new Vector[colors.length];
      
      while (steps-- > 0) {
        for (int i = 0; i < colors.length; i++) {
          vectors[i] = new Vector(0, 0, 0);
        }
        for (int i = 0; i < colors.length; i++) {
          Double[] colorA = colors[i];
          for (int j = 0; j < i; j++) {
            Double[] colorB = colors[j];
            
            double dl = colorA[0] - colorB[0];
            double da = colorA[1] - colorB[1];
            double db = colorA[2] - colorB[2];
            double d = getColorDistance(colorA, colorB, distanceType);
            if(d > 0) {
              double force = repulsion / Math.pow(d, 2);
              vectors[i].dl += dl * force / d;
              vectors[i].da += da * force / d;
              vectors[i].db += db * force / d;
              
              vectors[j].dl -= dl * force / d;
              vectors[j].da -= da * force / d;
              vectors[j].db -= db * force / d;
            } else {
              vectors[j].dl += 2 - 4 * Math.random();
              vectors[j].da += 2 - 4 * Math.random();
              vectors[j].db += 2 - 4 * Math.random();
            }
          }
        }
        for (int i = 0; i < colors.length; i++) {
          Double[] color = colors[i];
          double displacement = speed * Math.sqrt(Math.pow(vectors[i].dl, 2) + Math.pow(vectors[i].da, 2) + Math.pow(vectors[i].db, 2));
          if (displacement > 0) {
            double ratio = speed * Math.min(0.1, displacement) / displacement;
            Double[] candidateLab = new Double[] {
                color[0] + vectors[i].dl * ratio,
                color[1] + vectors[i].da * ratio,
                color[2] + vectors[i].db * ratio
            };
            if (checkLab(candidateLab, checker)) {
              colors[i] = candidateLab;
            }
          }
        }
      }
      Integer[] out = new Integer[colors.length];
      for (int i = 0; i < colors.length; i++) {
        out[i] = new LAB(colors[i][0], colors[i][1], colors[i][2]).rgb();
      }
      return out;
    }
    
    
    return null;
  }
  
  private static boolean checkLab(Double[] lab, Function checkColor) {
    return LAB.isInRGBGamut(lab[0], lab[1], lab[2])
        && checkColor.apply(Color.HEX(new LAB(lab[0], lab[1], lab[2]).rgb()));
  }
  
  
  private static double getColorDistance(Double[] labA, Double[] labB, Type type) {
    switch (type) {
      case DEFAULT:
      case EUCLIDIAN:
        return euclidianDistance(labA, labB);
      case CMC:
        return cmcDistance(labA, labB, 2, 1);
      case COMPROMISE:
        return compromiseDistance(labA, labB);
      default:
        return distanceColorblind(labA, labB);
    }
  }
  
  private static double euclidianDistance(Double[] labA, Double[] labB) {
    return Math.sqrt(Math.pow(labA[0] - labB[0], 2) + Math.pow(labA[1] - labB[1], 2) + Math.pow(labA[2] - labB[2], 2));
  }
  
  private static double cmcDistance(Double[] labA, Double[] labB, int l, int c) {
    return 0;
  }
  
  private static double compromiseDistance(Double[] labA, Double[] labB) {
    return 0;
  }
  
  private static double distanceColorblind(Double[] labA, Double[] labB) {
    return 0;
  }
  
  public enum Type {
    DEFAULT,
    EUCLIDIAN,
    CMC,
    COMPROMISE,
    COLORBLIND
  }
  
  private static class Vector {
    double dl, da, db;
    
    public Vector(double dl, double da, double db) {
      this.dl = dl;
      this.da = da;
      this.db = db;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy