QColor.java

package com.github.celldynamics.quimp;

import org.scijava.vecmath.Color3f;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Just a class for colors, that does not use stupid floats represented as RGB in the range 0->1.
 *
 * @author tyson
 */
public class QColor {

  /**
   * The colour maps.
   */
  public static final String[] colourMaps = { "Summer", "Cool", "Hot", "Grey" };

  /**
   * Color component.
   */
  public double red;
  /**
   * Color component.
   */
  public double green;
  /**
   * Color component.
   */
  public double blue;

  /**
   * The Constant LOGGER.
   */
  static final Logger LOGGER = LoggerFactory.getLogger(QColor.class.getName());

  /**
   * Copy constructor.
   * 
   * @param src object to copy
   */
  public QColor(final QColor src) {
    red = src.red;
    green = src.green;
    blue = src.blue;
  }

  /**
   * Instantiates a new q color.
   *
   * @param r the r
   * @param g the g
   * @param b the b
   */
  public QColor(double r, double g, double b) {
    this.setRGB(r, g, b);
  }

  /**
   * Prints the.
   */
  public void print() {
    System.out.println("R:" + red + " G: " + green + " B: " + blue);
  }

  /*
   * (non-Javadoc)
   * 
   * @see java.lang.Object#hashCode()
   */
  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    long temp;
    temp = Double.doubleToLongBits(blue);
    result = prime * result + (int) (temp ^ (temp >>> 32));
    temp = Double.doubleToLongBits(green);
    result = prime * result + (int) (temp ^ (temp >>> 32));
    temp = Double.doubleToLongBits(red);
    result = prime * result + (int) (temp ^ (temp >>> 32));
    return result;
  }

  /*
   * (non-Javadoc)
   * 
   * @see java.lang.Object#equals(java.lang.Object)
   */
  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (obj == null) {
      return false;
    }
    if (!(obj instanceof QColor)) {
      return false;
    }
    QColor other = (QColor) obj;
    if (Double.doubleToLongBits(blue) != Double.doubleToLongBits(other.blue)) {
      return false;
    }
    if (Double.doubleToLongBits(green) != Double.doubleToLongBits(other.green)) {
      return false;
    }
    if (Double.doubleToLongBits(red) != Double.doubleToLongBits(other.red)) {
      return false;
    }
    return true;
  }

  /*
   * (non-Javadoc)
   * 
   * @see java.lang.Object#toString()
   */
  @Override
  public String toString() {
    return "QColor [red=" + red + ", green=" + green + ", blue=" + blue + "]";
  }

  /**
   * Sets the RGB.
   *
   * @param r the r
   * @param g the g
   * @param b the b
   */
  public void setRGB(double r, double g, double b) {
    red = r;
    blue = b;
    green = g;

    if (red > 1 || red < 0 || green > 1 || green < 0 || blue > 1 || blue < 0) {
      LOGGER.warn(
              "32-Warning! Colour out of range: " + "R:" + red + " G: " + green + " B: " + blue);
      red = 0;
      blue = 0;
      green = 0;
    }

  }

  /**
   * Convert color to SVG codes.
   * 
   * @return SVG color representation
   */
  public String getColorSVG() {

    return "rgb(" + (red * 100) + "%," + (green * 100) + "%," + (blue * 100) + "%)";
  }

  /**
   * Gets the red.
   *
   * @return the red
   */
  public double getRed() {
    return red;
  }

  /**
   * Gets the red.
   *
   * @param bits the bits
   * @return the red - returns the color with a color space of size 'bits'
   */
  public int getRed(int bits) {
    return (int) Math.round(red * bits);
  }

  /**
   * Gets the green.
   *
   * @return the green
   */
  public double getGreen() {
    return green;
  }

  /**
   * Gets the green.
   *
   * @param bits the bits
   * @return the green - returns the color with a color space of size 'bits'
   */
  public int getGreen(int bits) {
    return (int) Math.round(green * bits);
  }

  /**
   * Gets the blue.
   *
   * @return the blue
   */
  public double getBlue() {
    return blue;
  }

  /**
   * Gets the blue.
   *
   * @param bits the bits
   * @return the blue - returns the color with a color space of size 'bits'
   */
  public int getBlue(int bits) {
    return (int) Math.round(blue * bits);
  }

  /**
   * Gets the color int.
   *
   * @return the color int
   */
  public int getColorInt() {
    return getRed(255) * 65536 + getGreen(255) * 256 + getBlue(255);
  }

  /**
   * Color int 23 f.
   *
   * @param c the c
   * @return the color 3 f
   */
  public static Color3f colorInt23f(int c) {
    float blueF = c % 256;
    int s = (int) (c / 256);
    float greenF = s % 256;
    s = (int) (c / 256) / 256;
    float redF = s % 256;

    redF = redF / 255;
    greenF = greenF / 255;
    blueF = blueF / 255;

    return new Color3f(redF, greenF, blueF);
  }

  /**
   * Gets the color BW.
   *
   * @param bits the bits
   * @return the color BW
   */
  public int getColorBW(int bits) {
    return (int) Math.round(((red + green + blue) / 3) * bits);
  }

  /**
   * ER color map 2.
   *
   * @param c the c
   * @param d the d
   * @param min the min
   * @param max the max
   * @return the q color
   */
  public static QColor erColorMap2(String c, double d, double min, double max) {
    if (c.equals("rwb")) {
      return rwbMap(d, max, min);
    } else if (c.equals("rbb")) {
      return rbbMap(d, max, min);
    } else {
      return rwbMap(d, max, min);
    }
  }

  /**
   * RWB map.
   *
   * @param d the value
   * @param max the max
   * @param min the min
   * @return the q color for d
   */
  public static QColor rwbMap(double d, double max, double min) {
    double r = 1;
    double g = 1;
    double b = 1;

    if (min == 0 || max == 0) {
      LOGGER.error("Min or max value is 0, returnig default color");
      return new QColor(r, g, b);
    }

    if (d == 0) {
      return new QColor(1.0, 1.0, 1.0);
    } else if (d > 0) { // red
      r = 1;

      double p = (1 / max) * d;
      if (p > 0.5) {
        g = (1 - p) * 2;
        b = 0;
      } else {
        g = 1;
        b = (0.5 - p) * 2;
      }

      return new QColor(r, g, b);
    } else { // blu
      b = 1;

      double p = (1 / min) * d;

      if (p > 0.5) {
        g = (1 - p) * 2;
        r = 0;
      } else {
        g = 1;
        r = (0.5 - p) * 2;
      }

      return new QColor(r, g, b);
    }

  }

  private static QColor rbbMap(double d, double max, double min) {
    double r = 0;
    double g = 0;
    double b = 0;

    if (d > max || d < min) {
      LOGGER.warn("Min == Max or d(" + d + ") not in min(" + min + ") or max(" + max + ")");
    }

    if (d == 0) { // black
      return new QColor(0.0, 0.0, 0.0);
    } else if (d > 0) { // red
      double p = (1 / max) * d;

      if (p >= 0.5) {
        g = (p - 0.5) * 2;
        r = 1;
      } else {
        g = 0;
        r = p * 2;
      }

      return new QColor(r, g, b);
    } else { // blu

      double p = (1 / min) * d;

      if (p >= 0.5) {
        g = (p - 0.5) * 2;
        b = 1;
      } else {
        g = 0;
        b = p * 2;
      }
      return new QColor(r, g, b);
    }

  }

  /**
   * RB blut.
   *
   * @param reds the reds
   * @param greens the greens
   * @param blues the blues
   * @return the int
   */
  static int RBBlut(byte[] reds, byte[] greens, byte[] blues) {
    int[] r = { 0, 0, 1, 25, 49, 73, 98, 122, 146, 162, 173, 184, 195, 207, 217, 229, 240, 252, 255,
        255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 };
    int[] g = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 35, 57, 79, 101, 117, 133, 147, 161, 175,
        190, 205, 219, 234, 248, 255, 255, 255, 255 };
    int[] b = { 0, 61, 96, 130, 165, 192, 220, 227, 210, 181, 151, 122, 93, 64, 35, 5, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 35, 98, 160, 223, 255 };
    for (int i = 0; i < r.length; i++) {
      reds[i] = (byte) r[i];
      greens[i] = (byte) g[i];
      blues[i] = (byte) b[i];
    }
    return r.length;
  }

  /**
   * Bw scale.
   *
   * @param value the value
   * @param bits the bits
   * @param max the max
   * @param min the min
   * @return the int
   */
  public static int bwScale(double value, int bits, double max, double min) {
    if (value > max || value < min) {
      // System.out.println("Warning! bwScale value not in range:
      // "+value+" (min:"+ min +", max:" + max+")");
      Exception e = new Exception();
      e.printStackTrace();
      System.exit(1);
      return 0;
    }

    return (int) Math.round((value - min) * ((bits - 1) / (max - min)));
    // if(value==255)System.out.println("value: " + value+", min:" + min +
    // ", max:" + max+", bits:" + bits+",temp: "+temp+", answ: " + answ);

  }

  /**
   * Create a color map.
   * 
   * @param c choice of color map: "Summer"
   * @param size size of the color map
   * @return QColor array of length 'size'
   */
  public static QColor[] colourMap(String c, int size) {
    QColor[] map = new QColor[size];
    double r;
    double b;
    double g;

    // set default colour
    if (c == null || c.isEmpty()) {
      c = "Summer";
    }
    if (c.equals("Summer")) {
      r = 0.;
      g = 0.5;
      b = 0.4;
      map[0] = new QColor(r, g, b);

      double dr = 1d / (double) size;
      double dg = 0.5d / (double) size;

      for (int i = 1; i < size; i++) {
        r += dr;
        g += dg;
        map[i] = new QColor(r, g, b);
      }
    } else if (c.equals("Cool")) {
      r = 0.;
      b = 0.5;
      g = 0.4;
      map[0] = new QColor(r, g, b);

      double dr = 1d / (double) size;
      double db = 0.5d / (double) size;

      for (int i = 1; i < size; i++) {
        r += dr;
        b += db;
        map[i] = new QColor(r, g, b);
      }
    } else if (c.equals("Hot")) {
      // based on matlab code for Hot
      r = 0.2d;
      g = 0d;
      b = 0;
      map[0] = new QColor(r, g, b);

      double n = Math.floor(3d / 8d * size);

      for (int i = 0; i < size; i++) {

        r = (i + 1) / n;
        if (r > 1) {
          r = 1d;
        }

        if (i < n) {
          g = 0d;
        } else {
          g = (i - n + 1) / n;
        }
        if (g > 1) {
          g = 1d;
        }

        if (i < 2 * n) {
          b = 0d;
        } else {
          b = (1 + i - 2 * n) / (size - (2 * n));
        }
        if (b > 1) {
          b = 1d;
        }
        map[i] = new QColor(r, g, b);
      }

    } else if (c.equals("Grey")) {
      r = 0.1d;
      g = 0.1d;
      b = 0.1d;
      map[0] = new QColor(r, g, b);

      double d = 0.9d / (double) size;

      for (int i = 1; i < size; i++) {
        r += d;
        g += d;
        b += d;
        map[i] = new QColor(r, g, b);
      }

    } else {
      for (int i = 0; i < size; i++) {
        map[i] = new QColor(0.5, 0.5, 0.5);
      }
      System.out.println("warning: unknown color map: " + c);
    }

    return map;
  }

  /**
   * Get a random light color.
   * 
   * @return random color
   */
  public static QColor lightColor() {
    double r = (Math.random() / 1.3) + 0.23;
    double g = (Math.random() / 1.3) + 0.23;
    double b = (Math.random() / 1.3) + 0.23;

    return new QColor(r, g, b);
  }

}