/******************************************************************************* * Copyright 2017 Ivan Shubin http://galenframework.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.galenframework.rainbow4j.colorscheme; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import java.awt.*; import java.util.List; import java.util.stream.Collectors; public class GradientColorClassifier implements ColorClassifier { private final Integer [][] colors; private String name; public GradientColorClassifier(String name, List<Color> colors) { this.name = name; this.colors = colors.stream().map(c -> new Integer[]{c.getRed(), c.getGreen(), c.getBlue()} ).collect(Collectors.toList()).toArray(new Integer[][]{}); } @Override public String getName() { return name; } @Override public boolean holdsColor(int r, int g, int b, int maxColorSquareDistance) { for (int i = 1; i < colors.length; i++) { if (holdsColorBetweenPoints(r, g, b, colors[i-1], colors[i], maxColorSquareDistance)) { return true; } } return false; } private boolean holdsColorBetweenPoints(int r, int g, int b, Integer[] g1, Integer[] g2, int maxDistance) { double Gr = g2[0] - g1[0]; double Gg = g2[1] - g1[1]; double Gb = g2[2] - g1[2]; double errorRate = 16; double Gsquare = Gr*Gr + Gg*Gg + Gb*Gb; if (Gsquare > 0) { double Vr = r - g1[0]; double Vg = g - g1[1]; double Vb = b - g1[2]; double K = (Vr*Gr + Vg*Gg + Vb*Gb); // calculating projection vector double Vpr = K*Gr/Gsquare; double Vpg = K*Gg/Gsquare; double Vpb = K*Gb/Gsquare; // checking whether projection will be between the points double Vpsquare = Vpr*Vpr + Vpg*Vpg + Vpb*Vpb; if (Vpsquare - errorRate > Gsquare) { if (Math.sqrt(Vpsquare) - Math.sqrt(Gsquare) > errorRate) { return false; } } double VpPlusGsquare = (Vpr + Gr)*(Vpr + Gr) + (Vpg + Gg)*(Vpg + Gg) + (Vpb + Gb)*(Vpb + Gb); if (VpPlusGsquare < Gsquare) { if (Vpsquare > errorRate) { return false; } } // Checking if distance from point to projection is within allowed range double Pr = Vpr + g1[0]; double Pg = Vpg + g1[1]; double Pb = Vpb + g1[2]; double Dr = r - Pr; double Dg = g - Pg; double Db = b - Pb; double D = Dr*Dr + Dg*Dg + Db*Db; if (D < maxDistance) { return true; } } return false; } @Override public int hashCode() { return new HashCodeBuilder() .append(colors) .append(name) .toHashCode(); } @Override public boolean equals(Object obj) { if (obj == null) return false; if (obj == this) return true; if (!(obj instanceof GradientColorClassifier)) return false; GradientColorClassifier rhs = (GradientColorClassifier)obj; return new EqualsBuilder() .append(rhs.colors, this.colors) .append(rhs.name, this.name) .isEquals(); } @Override public String toString() { return new ToStringBuilder(this) .append("colors", colors) .append("name", name) .toString(); } }