1*970e1046SAndroid Build Coastguard Worker /* 2*970e1046SAndroid Build Coastguard Worker * Copyright 2021 Google LLC 3*970e1046SAndroid Build Coastguard Worker * 4*970e1046SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*970e1046SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*970e1046SAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*970e1046SAndroid Build Coastguard Worker * 8*970e1046SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*970e1046SAndroid Build Coastguard Worker * 10*970e1046SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*970e1046SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*970e1046SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*970e1046SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*970e1046SAndroid Build Coastguard Worker * limitations under the License. 15*970e1046SAndroid Build Coastguard Worker */ 16*970e1046SAndroid Build Coastguard Worker 17*970e1046SAndroid Build Coastguard Worker package com.google.ux.material.libmonet.quantize; 18*970e1046SAndroid Build Coastguard Worker 19*970e1046SAndroid Build Coastguard Worker import com.google.ux.material.libmonet.utils.ColorUtils; 20*970e1046SAndroid Build Coastguard Worker import java.util.ArrayList; 21*970e1046SAndroid Build Coastguard Worker import java.util.LinkedHashMap; 22*970e1046SAndroid Build Coastguard Worker import java.util.List; 23*970e1046SAndroid Build Coastguard Worker import java.util.Map; 24*970e1046SAndroid Build Coastguard Worker 25*970e1046SAndroid Build Coastguard Worker /** 26*970e1046SAndroid Build Coastguard Worker * An image quantizer that divides the image's pixels into clusters by recursively cutting an RGB 27*970e1046SAndroid Build Coastguard Worker * cube, based on the weight of pixels in each area of the cube. 28*970e1046SAndroid Build Coastguard Worker * 29*970e1046SAndroid Build Coastguard Worker * <p>The algorithm was described by Xiaolin Wu in Graphic Gems II, published in 1991. 30*970e1046SAndroid Build Coastguard Worker */ 31*970e1046SAndroid Build Coastguard Worker public final class QuantizerWu implements Quantizer { 32*970e1046SAndroid Build Coastguard Worker int[] weights; 33*970e1046SAndroid Build Coastguard Worker int[] momentsR; 34*970e1046SAndroid Build Coastguard Worker int[] momentsG; 35*970e1046SAndroid Build Coastguard Worker int[] momentsB; 36*970e1046SAndroid Build Coastguard Worker double[] moments; 37*970e1046SAndroid Build Coastguard Worker Box[] cubes; 38*970e1046SAndroid Build Coastguard Worker 39*970e1046SAndroid Build Coastguard Worker // A histogram of all the input colors is constructed. It has the shape of a 40*970e1046SAndroid Build Coastguard Worker // cube. The cube would be too large if it contained all 16 million colors: 41*970e1046SAndroid Build Coastguard Worker // historical best practice is to use 5 bits of the 8 in each channel, 42*970e1046SAndroid Build Coastguard Worker // reducing the histogram to a volume of ~32,000. 43*970e1046SAndroid Build Coastguard Worker private static final int INDEX_BITS = 5; 44*970e1046SAndroid Build Coastguard Worker private static final int INDEX_COUNT = 33; // ((1 << INDEX_BITS) + 1) 45*970e1046SAndroid Build Coastguard Worker private static final int TOTAL_SIZE = 35937; // INDEX_COUNT * INDEX_COUNT * INDEX_COUNT 46*970e1046SAndroid Build Coastguard Worker 47*970e1046SAndroid Build Coastguard Worker @Override quantize(int[] pixels, int colorCount)48*970e1046SAndroid Build Coastguard Worker public QuantizerResult quantize(int[] pixels, int colorCount) { 49*970e1046SAndroid Build Coastguard Worker QuantizerResult mapResult = new QuantizerMap().quantize(pixels, colorCount); 50*970e1046SAndroid Build Coastguard Worker constructHistogram(mapResult.colorToCount); 51*970e1046SAndroid Build Coastguard Worker createMoments(); 52*970e1046SAndroid Build Coastguard Worker CreateBoxesResult createBoxesResult = createBoxes(colorCount); 53*970e1046SAndroid Build Coastguard Worker List<Integer> colors = createResult(createBoxesResult.resultCount); 54*970e1046SAndroid Build Coastguard Worker Map<Integer, Integer> resultMap = new LinkedHashMap<>(); 55*970e1046SAndroid Build Coastguard Worker for (int color : colors) { 56*970e1046SAndroid Build Coastguard Worker resultMap.put(color, 0); 57*970e1046SAndroid Build Coastguard Worker } 58*970e1046SAndroid Build Coastguard Worker return new QuantizerResult(resultMap); 59*970e1046SAndroid Build Coastguard Worker } 60*970e1046SAndroid Build Coastguard Worker getIndex(int r, int g, int b)61*970e1046SAndroid Build Coastguard Worker static int getIndex(int r, int g, int b) { 62*970e1046SAndroid Build Coastguard Worker return (r << (INDEX_BITS * 2)) + (r << (INDEX_BITS + 1)) + r + (g << INDEX_BITS) + g + b; 63*970e1046SAndroid Build Coastguard Worker } 64*970e1046SAndroid Build Coastguard Worker constructHistogram(Map<Integer, Integer> pixels)65*970e1046SAndroid Build Coastguard Worker void constructHistogram(Map<Integer, Integer> pixels) { 66*970e1046SAndroid Build Coastguard Worker weights = new int[TOTAL_SIZE]; 67*970e1046SAndroid Build Coastguard Worker momentsR = new int[TOTAL_SIZE]; 68*970e1046SAndroid Build Coastguard Worker momentsG = new int[TOTAL_SIZE]; 69*970e1046SAndroid Build Coastguard Worker momentsB = new int[TOTAL_SIZE]; 70*970e1046SAndroid Build Coastguard Worker moments = new double[TOTAL_SIZE]; 71*970e1046SAndroid Build Coastguard Worker 72*970e1046SAndroid Build Coastguard Worker for (Map.Entry<Integer, Integer> pair : pixels.entrySet()) { 73*970e1046SAndroid Build Coastguard Worker int pixel = pair.getKey(); 74*970e1046SAndroid Build Coastguard Worker int count = pair.getValue(); 75*970e1046SAndroid Build Coastguard Worker int red = ColorUtils.redFromArgb(pixel); 76*970e1046SAndroid Build Coastguard Worker int green = ColorUtils.greenFromArgb(pixel); 77*970e1046SAndroid Build Coastguard Worker int blue = ColorUtils.blueFromArgb(pixel); 78*970e1046SAndroid Build Coastguard Worker int bitsToRemove = 8 - INDEX_BITS; 79*970e1046SAndroid Build Coastguard Worker int iR = (red >> bitsToRemove) + 1; 80*970e1046SAndroid Build Coastguard Worker int iG = (green >> bitsToRemove) + 1; 81*970e1046SAndroid Build Coastguard Worker int iB = (blue >> bitsToRemove) + 1; 82*970e1046SAndroid Build Coastguard Worker int index = getIndex(iR, iG, iB); 83*970e1046SAndroid Build Coastguard Worker weights[index] += count; 84*970e1046SAndroid Build Coastguard Worker momentsR[index] += (red * count); 85*970e1046SAndroid Build Coastguard Worker momentsG[index] += (green * count); 86*970e1046SAndroid Build Coastguard Worker momentsB[index] += (blue * count); 87*970e1046SAndroid Build Coastguard Worker moments[index] += (count * ((red * red) + (green * green) + (blue * blue))); 88*970e1046SAndroid Build Coastguard Worker } 89*970e1046SAndroid Build Coastguard Worker } 90*970e1046SAndroid Build Coastguard Worker createMoments()91*970e1046SAndroid Build Coastguard Worker void createMoments() { 92*970e1046SAndroid Build Coastguard Worker for (int r = 1; r < INDEX_COUNT; ++r) { 93*970e1046SAndroid Build Coastguard Worker int[] area = new int[INDEX_COUNT]; 94*970e1046SAndroid Build Coastguard Worker int[] areaR = new int[INDEX_COUNT]; 95*970e1046SAndroid Build Coastguard Worker int[] areaG = new int[INDEX_COUNT]; 96*970e1046SAndroid Build Coastguard Worker int[] areaB = new int[INDEX_COUNT]; 97*970e1046SAndroid Build Coastguard Worker double[] area2 = new double[INDEX_COUNT]; 98*970e1046SAndroid Build Coastguard Worker 99*970e1046SAndroid Build Coastguard Worker for (int g = 1; g < INDEX_COUNT; ++g) { 100*970e1046SAndroid Build Coastguard Worker int line = 0; 101*970e1046SAndroid Build Coastguard Worker int lineR = 0; 102*970e1046SAndroid Build Coastguard Worker int lineG = 0; 103*970e1046SAndroid Build Coastguard Worker int lineB = 0; 104*970e1046SAndroid Build Coastguard Worker double line2 = 0.0; 105*970e1046SAndroid Build Coastguard Worker for (int b = 1; b < INDEX_COUNT; ++b) { 106*970e1046SAndroid Build Coastguard Worker int index = getIndex(r, g, b); 107*970e1046SAndroid Build Coastguard Worker line += weights[index]; 108*970e1046SAndroid Build Coastguard Worker lineR += momentsR[index]; 109*970e1046SAndroid Build Coastguard Worker lineG += momentsG[index]; 110*970e1046SAndroid Build Coastguard Worker lineB += momentsB[index]; 111*970e1046SAndroid Build Coastguard Worker line2 += moments[index]; 112*970e1046SAndroid Build Coastguard Worker 113*970e1046SAndroid Build Coastguard Worker area[b] += line; 114*970e1046SAndroid Build Coastguard Worker areaR[b] += lineR; 115*970e1046SAndroid Build Coastguard Worker areaG[b] += lineG; 116*970e1046SAndroid Build Coastguard Worker areaB[b] += lineB; 117*970e1046SAndroid Build Coastguard Worker area2[b] += line2; 118*970e1046SAndroid Build Coastguard Worker 119*970e1046SAndroid Build Coastguard Worker int previousIndex = getIndex(r - 1, g, b); 120*970e1046SAndroid Build Coastguard Worker weights[index] = weights[previousIndex] + area[b]; 121*970e1046SAndroid Build Coastguard Worker momentsR[index] = momentsR[previousIndex] + areaR[b]; 122*970e1046SAndroid Build Coastguard Worker momentsG[index] = momentsG[previousIndex] + areaG[b]; 123*970e1046SAndroid Build Coastguard Worker momentsB[index] = momentsB[previousIndex] + areaB[b]; 124*970e1046SAndroid Build Coastguard Worker moments[index] = moments[previousIndex] + area2[b]; 125*970e1046SAndroid Build Coastguard Worker } 126*970e1046SAndroid Build Coastguard Worker } 127*970e1046SAndroid Build Coastguard Worker } 128*970e1046SAndroid Build Coastguard Worker } 129*970e1046SAndroid Build Coastguard Worker createBoxes(int maxColorCount)130*970e1046SAndroid Build Coastguard Worker CreateBoxesResult createBoxes(int maxColorCount) { 131*970e1046SAndroid Build Coastguard Worker cubes = new Box[maxColorCount]; 132*970e1046SAndroid Build Coastguard Worker for (int i = 0; i < maxColorCount; i++) { 133*970e1046SAndroid Build Coastguard Worker cubes[i] = new Box(); 134*970e1046SAndroid Build Coastguard Worker } 135*970e1046SAndroid Build Coastguard Worker double[] volumeVariance = new double[maxColorCount]; 136*970e1046SAndroid Build Coastguard Worker Box firstBox = cubes[0]; 137*970e1046SAndroid Build Coastguard Worker firstBox.r1 = INDEX_COUNT - 1; 138*970e1046SAndroid Build Coastguard Worker firstBox.g1 = INDEX_COUNT - 1; 139*970e1046SAndroid Build Coastguard Worker firstBox.b1 = INDEX_COUNT - 1; 140*970e1046SAndroid Build Coastguard Worker 141*970e1046SAndroid Build Coastguard Worker int generatedColorCount = maxColorCount; 142*970e1046SAndroid Build Coastguard Worker int next = 0; 143*970e1046SAndroid Build Coastguard Worker for (int i = 1; i < maxColorCount; i++) { 144*970e1046SAndroid Build Coastguard Worker if (cut(cubes[next], cubes[i])) { 145*970e1046SAndroid Build Coastguard Worker volumeVariance[next] = (cubes[next].vol > 1) ? variance(cubes[next]) : 0.0; 146*970e1046SAndroid Build Coastguard Worker volumeVariance[i] = (cubes[i].vol > 1) ? variance(cubes[i]) : 0.0; 147*970e1046SAndroid Build Coastguard Worker } else { 148*970e1046SAndroid Build Coastguard Worker volumeVariance[next] = 0.0; 149*970e1046SAndroid Build Coastguard Worker i--; 150*970e1046SAndroid Build Coastguard Worker } 151*970e1046SAndroid Build Coastguard Worker 152*970e1046SAndroid Build Coastguard Worker next = 0; 153*970e1046SAndroid Build Coastguard Worker 154*970e1046SAndroid Build Coastguard Worker double temp = volumeVariance[0]; 155*970e1046SAndroid Build Coastguard Worker for (int j = 1; j <= i; j++) { 156*970e1046SAndroid Build Coastguard Worker if (volumeVariance[j] > temp) { 157*970e1046SAndroid Build Coastguard Worker temp = volumeVariance[j]; 158*970e1046SAndroid Build Coastguard Worker next = j; 159*970e1046SAndroid Build Coastguard Worker } 160*970e1046SAndroid Build Coastguard Worker } 161*970e1046SAndroid Build Coastguard Worker if (temp <= 0.0) { 162*970e1046SAndroid Build Coastguard Worker generatedColorCount = i + 1; 163*970e1046SAndroid Build Coastguard Worker break; 164*970e1046SAndroid Build Coastguard Worker } 165*970e1046SAndroid Build Coastguard Worker } 166*970e1046SAndroid Build Coastguard Worker 167*970e1046SAndroid Build Coastguard Worker return new CreateBoxesResult(maxColorCount, generatedColorCount); 168*970e1046SAndroid Build Coastguard Worker } 169*970e1046SAndroid Build Coastguard Worker createResult(int colorCount)170*970e1046SAndroid Build Coastguard Worker List<Integer> createResult(int colorCount) { 171*970e1046SAndroid Build Coastguard Worker List<Integer> colors = new ArrayList<>(); 172*970e1046SAndroid Build Coastguard Worker for (int i = 0; i < colorCount; ++i) { 173*970e1046SAndroid Build Coastguard Worker Box cube = cubes[i]; 174*970e1046SAndroid Build Coastguard Worker int weight = volume(cube, weights); 175*970e1046SAndroid Build Coastguard Worker if (weight > 0) { 176*970e1046SAndroid Build Coastguard Worker int r = volume(cube, momentsR) / weight; 177*970e1046SAndroid Build Coastguard Worker int g = volume(cube, momentsG) / weight; 178*970e1046SAndroid Build Coastguard Worker int b = volume(cube, momentsB) / weight; 179*970e1046SAndroid Build Coastguard Worker int color = (255 << 24) | ((r & 0x0ff) << 16) | ((g & 0x0ff) << 8) | (b & 0x0ff); 180*970e1046SAndroid Build Coastguard Worker colors.add(color); 181*970e1046SAndroid Build Coastguard Worker } 182*970e1046SAndroid Build Coastguard Worker } 183*970e1046SAndroid Build Coastguard Worker return colors; 184*970e1046SAndroid Build Coastguard Worker } 185*970e1046SAndroid Build Coastguard Worker variance(Box cube)186*970e1046SAndroid Build Coastguard Worker double variance(Box cube) { 187*970e1046SAndroid Build Coastguard Worker int dr = volume(cube, momentsR); 188*970e1046SAndroid Build Coastguard Worker int dg = volume(cube, momentsG); 189*970e1046SAndroid Build Coastguard Worker int db = volume(cube, momentsB); 190*970e1046SAndroid Build Coastguard Worker double xx = 191*970e1046SAndroid Build Coastguard Worker moments[getIndex(cube.r1, cube.g1, cube.b1)] 192*970e1046SAndroid Build Coastguard Worker - moments[getIndex(cube.r1, cube.g1, cube.b0)] 193*970e1046SAndroid Build Coastguard Worker - moments[getIndex(cube.r1, cube.g0, cube.b1)] 194*970e1046SAndroid Build Coastguard Worker + moments[getIndex(cube.r1, cube.g0, cube.b0)] 195*970e1046SAndroid Build Coastguard Worker - moments[getIndex(cube.r0, cube.g1, cube.b1)] 196*970e1046SAndroid Build Coastguard Worker + moments[getIndex(cube.r0, cube.g1, cube.b0)] 197*970e1046SAndroid Build Coastguard Worker + moments[getIndex(cube.r0, cube.g0, cube.b1)] 198*970e1046SAndroid Build Coastguard Worker - moments[getIndex(cube.r0, cube.g0, cube.b0)]; 199*970e1046SAndroid Build Coastguard Worker 200*970e1046SAndroid Build Coastguard Worker int hypotenuse = dr * dr + dg * dg + db * db; 201*970e1046SAndroid Build Coastguard Worker int volume = volume(cube, weights); 202*970e1046SAndroid Build Coastguard Worker return xx - hypotenuse / ((double) volume); 203*970e1046SAndroid Build Coastguard Worker } 204*970e1046SAndroid Build Coastguard Worker cut(Box one, Box two)205*970e1046SAndroid Build Coastguard Worker Boolean cut(Box one, Box two) { 206*970e1046SAndroid Build Coastguard Worker int wholeR = volume(one, momentsR); 207*970e1046SAndroid Build Coastguard Worker int wholeG = volume(one, momentsG); 208*970e1046SAndroid Build Coastguard Worker int wholeB = volume(one, momentsB); 209*970e1046SAndroid Build Coastguard Worker int wholeW = volume(one, weights); 210*970e1046SAndroid Build Coastguard Worker 211*970e1046SAndroid Build Coastguard Worker MaximizeResult maxRResult = 212*970e1046SAndroid Build Coastguard Worker maximize(one, Direction.RED, one.r0 + 1, one.r1, wholeR, wholeG, wholeB, wholeW); 213*970e1046SAndroid Build Coastguard Worker MaximizeResult maxGResult = 214*970e1046SAndroid Build Coastguard Worker maximize(one, Direction.GREEN, one.g0 + 1, one.g1, wholeR, wholeG, wholeB, wholeW); 215*970e1046SAndroid Build Coastguard Worker MaximizeResult maxBResult = 216*970e1046SAndroid Build Coastguard Worker maximize(one, Direction.BLUE, one.b0 + 1, one.b1, wholeR, wholeG, wholeB, wholeW); 217*970e1046SAndroid Build Coastguard Worker Direction cutDirection; 218*970e1046SAndroid Build Coastguard Worker double maxR = maxRResult.maximum; 219*970e1046SAndroid Build Coastguard Worker double maxG = maxGResult.maximum; 220*970e1046SAndroid Build Coastguard Worker double maxB = maxBResult.maximum; 221*970e1046SAndroid Build Coastguard Worker if (maxR >= maxG && maxR >= maxB) { 222*970e1046SAndroid Build Coastguard Worker if (maxRResult.cutLocation < 0) { 223*970e1046SAndroid Build Coastguard Worker return false; 224*970e1046SAndroid Build Coastguard Worker } 225*970e1046SAndroid Build Coastguard Worker cutDirection = Direction.RED; 226*970e1046SAndroid Build Coastguard Worker } else if (maxG >= maxR && maxG >= maxB) { 227*970e1046SAndroid Build Coastguard Worker cutDirection = Direction.GREEN; 228*970e1046SAndroid Build Coastguard Worker } else { 229*970e1046SAndroid Build Coastguard Worker cutDirection = Direction.BLUE; 230*970e1046SAndroid Build Coastguard Worker } 231*970e1046SAndroid Build Coastguard Worker 232*970e1046SAndroid Build Coastguard Worker two.r1 = one.r1; 233*970e1046SAndroid Build Coastguard Worker two.g1 = one.g1; 234*970e1046SAndroid Build Coastguard Worker two.b1 = one.b1; 235*970e1046SAndroid Build Coastguard Worker 236*970e1046SAndroid Build Coastguard Worker switch (cutDirection) { 237*970e1046SAndroid Build Coastguard Worker case RED: 238*970e1046SAndroid Build Coastguard Worker one.r1 = maxRResult.cutLocation; 239*970e1046SAndroid Build Coastguard Worker two.r0 = one.r1; 240*970e1046SAndroid Build Coastguard Worker two.g0 = one.g0; 241*970e1046SAndroid Build Coastguard Worker two.b0 = one.b0; 242*970e1046SAndroid Build Coastguard Worker break; 243*970e1046SAndroid Build Coastguard Worker case GREEN: 244*970e1046SAndroid Build Coastguard Worker one.g1 = maxGResult.cutLocation; 245*970e1046SAndroid Build Coastguard Worker two.r0 = one.r0; 246*970e1046SAndroid Build Coastguard Worker two.g0 = one.g1; 247*970e1046SAndroid Build Coastguard Worker two.b0 = one.b0; 248*970e1046SAndroid Build Coastguard Worker break; 249*970e1046SAndroid Build Coastguard Worker case BLUE: 250*970e1046SAndroid Build Coastguard Worker one.b1 = maxBResult.cutLocation; 251*970e1046SAndroid Build Coastguard Worker two.r0 = one.r0; 252*970e1046SAndroid Build Coastguard Worker two.g0 = one.g0; 253*970e1046SAndroid Build Coastguard Worker two.b0 = one.b1; 254*970e1046SAndroid Build Coastguard Worker break; 255*970e1046SAndroid Build Coastguard Worker } 256*970e1046SAndroid Build Coastguard Worker 257*970e1046SAndroid Build Coastguard Worker one.vol = (one.r1 - one.r0) * (one.g1 - one.g0) * (one.b1 - one.b0); 258*970e1046SAndroid Build Coastguard Worker two.vol = (two.r1 - two.r0) * (two.g1 - two.g0) * (two.b1 - two.b0); 259*970e1046SAndroid Build Coastguard Worker 260*970e1046SAndroid Build Coastguard Worker return true; 261*970e1046SAndroid Build Coastguard Worker } 262*970e1046SAndroid Build Coastguard Worker maximize( Box cube, Direction direction, int first, int last, int wholeR, int wholeG, int wholeB, int wholeW)263*970e1046SAndroid Build Coastguard Worker MaximizeResult maximize( 264*970e1046SAndroid Build Coastguard Worker Box cube, 265*970e1046SAndroid Build Coastguard Worker Direction direction, 266*970e1046SAndroid Build Coastguard Worker int first, 267*970e1046SAndroid Build Coastguard Worker int last, 268*970e1046SAndroid Build Coastguard Worker int wholeR, 269*970e1046SAndroid Build Coastguard Worker int wholeG, 270*970e1046SAndroid Build Coastguard Worker int wholeB, 271*970e1046SAndroid Build Coastguard Worker int wholeW) { 272*970e1046SAndroid Build Coastguard Worker int bottomR = bottom(cube, direction, momentsR); 273*970e1046SAndroid Build Coastguard Worker int bottomG = bottom(cube, direction, momentsG); 274*970e1046SAndroid Build Coastguard Worker int bottomB = bottom(cube, direction, momentsB); 275*970e1046SAndroid Build Coastguard Worker int bottomW = bottom(cube, direction, weights); 276*970e1046SAndroid Build Coastguard Worker 277*970e1046SAndroid Build Coastguard Worker double max = 0.0; 278*970e1046SAndroid Build Coastguard Worker int cut = -1; 279*970e1046SAndroid Build Coastguard Worker 280*970e1046SAndroid Build Coastguard Worker int halfR = 0; 281*970e1046SAndroid Build Coastguard Worker int halfG = 0; 282*970e1046SAndroid Build Coastguard Worker int halfB = 0; 283*970e1046SAndroid Build Coastguard Worker int halfW = 0; 284*970e1046SAndroid Build Coastguard Worker for (int i = first; i < last; i++) { 285*970e1046SAndroid Build Coastguard Worker halfR = bottomR + top(cube, direction, i, momentsR); 286*970e1046SAndroid Build Coastguard Worker halfG = bottomG + top(cube, direction, i, momentsG); 287*970e1046SAndroid Build Coastguard Worker halfB = bottomB + top(cube, direction, i, momentsB); 288*970e1046SAndroid Build Coastguard Worker halfW = bottomW + top(cube, direction, i, weights); 289*970e1046SAndroid Build Coastguard Worker if (halfW == 0) { 290*970e1046SAndroid Build Coastguard Worker continue; 291*970e1046SAndroid Build Coastguard Worker } 292*970e1046SAndroid Build Coastguard Worker 293*970e1046SAndroid Build Coastguard Worker double tempNumerator = halfR * halfR + halfG * halfG + halfB * halfB; 294*970e1046SAndroid Build Coastguard Worker double tempDenominator = halfW; 295*970e1046SAndroid Build Coastguard Worker double temp = tempNumerator / tempDenominator; 296*970e1046SAndroid Build Coastguard Worker 297*970e1046SAndroid Build Coastguard Worker halfR = wholeR - halfR; 298*970e1046SAndroid Build Coastguard Worker halfG = wholeG - halfG; 299*970e1046SAndroid Build Coastguard Worker halfB = wholeB - halfB; 300*970e1046SAndroid Build Coastguard Worker halfW = wholeW - halfW; 301*970e1046SAndroid Build Coastguard Worker if (halfW == 0) { 302*970e1046SAndroid Build Coastguard Worker continue; 303*970e1046SAndroid Build Coastguard Worker } 304*970e1046SAndroid Build Coastguard Worker 305*970e1046SAndroid Build Coastguard Worker tempNumerator = halfR * halfR + halfG * halfG + halfB * halfB; 306*970e1046SAndroid Build Coastguard Worker tempDenominator = halfW; 307*970e1046SAndroid Build Coastguard Worker temp += (tempNumerator / tempDenominator); 308*970e1046SAndroid Build Coastguard Worker 309*970e1046SAndroid Build Coastguard Worker if (temp > max) { 310*970e1046SAndroid Build Coastguard Worker max = temp; 311*970e1046SAndroid Build Coastguard Worker cut = i; 312*970e1046SAndroid Build Coastguard Worker } 313*970e1046SAndroid Build Coastguard Worker } 314*970e1046SAndroid Build Coastguard Worker return new MaximizeResult(cut, max); 315*970e1046SAndroid Build Coastguard Worker } 316*970e1046SAndroid Build Coastguard Worker volume(Box cube, int[] moment)317*970e1046SAndroid Build Coastguard Worker static int volume(Box cube, int[] moment) { 318*970e1046SAndroid Build Coastguard Worker return (moment[getIndex(cube.r1, cube.g1, cube.b1)] 319*970e1046SAndroid Build Coastguard Worker - moment[getIndex(cube.r1, cube.g1, cube.b0)] 320*970e1046SAndroid Build Coastguard Worker - moment[getIndex(cube.r1, cube.g0, cube.b1)] 321*970e1046SAndroid Build Coastguard Worker + moment[getIndex(cube.r1, cube.g0, cube.b0)] 322*970e1046SAndroid Build Coastguard Worker - moment[getIndex(cube.r0, cube.g1, cube.b1)] 323*970e1046SAndroid Build Coastguard Worker + moment[getIndex(cube.r0, cube.g1, cube.b0)] 324*970e1046SAndroid Build Coastguard Worker + moment[getIndex(cube.r0, cube.g0, cube.b1)] 325*970e1046SAndroid Build Coastguard Worker - moment[getIndex(cube.r0, cube.g0, cube.b0)]); 326*970e1046SAndroid Build Coastguard Worker } 327*970e1046SAndroid Build Coastguard Worker bottom(Box cube, Direction direction, int[] moment)328*970e1046SAndroid Build Coastguard Worker static int bottom(Box cube, Direction direction, int[] moment) { 329*970e1046SAndroid Build Coastguard Worker switch (direction) { 330*970e1046SAndroid Build Coastguard Worker case RED: 331*970e1046SAndroid Build Coastguard Worker return -moment[getIndex(cube.r0, cube.g1, cube.b1)] 332*970e1046SAndroid Build Coastguard Worker + moment[getIndex(cube.r0, cube.g1, cube.b0)] 333*970e1046SAndroid Build Coastguard Worker + moment[getIndex(cube.r0, cube.g0, cube.b1)] 334*970e1046SAndroid Build Coastguard Worker - moment[getIndex(cube.r0, cube.g0, cube.b0)]; 335*970e1046SAndroid Build Coastguard Worker case GREEN: 336*970e1046SAndroid Build Coastguard Worker return -moment[getIndex(cube.r1, cube.g0, cube.b1)] 337*970e1046SAndroid Build Coastguard Worker + moment[getIndex(cube.r1, cube.g0, cube.b0)] 338*970e1046SAndroid Build Coastguard Worker + moment[getIndex(cube.r0, cube.g0, cube.b1)] 339*970e1046SAndroid Build Coastguard Worker - moment[getIndex(cube.r0, cube.g0, cube.b0)]; 340*970e1046SAndroid Build Coastguard Worker case BLUE: 341*970e1046SAndroid Build Coastguard Worker return -moment[getIndex(cube.r1, cube.g1, cube.b0)] 342*970e1046SAndroid Build Coastguard Worker + moment[getIndex(cube.r1, cube.g0, cube.b0)] 343*970e1046SAndroid Build Coastguard Worker + moment[getIndex(cube.r0, cube.g1, cube.b0)] 344*970e1046SAndroid Build Coastguard Worker - moment[getIndex(cube.r0, cube.g0, cube.b0)]; 345*970e1046SAndroid Build Coastguard Worker } 346*970e1046SAndroid Build Coastguard Worker throw new IllegalArgumentException("unexpected direction " + direction); 347*970e1046SAndroid Build Coastguard Worker } 348*970e1046SAndroid Build Coastguard Worker top(Box cube, Direction direction, int position, int[] moment)349*970e1046SAndroid Build Coastguard Worker static int top(Box cube, Direction direction, int position, int[] moment) { 350*970e1046SAndroid Build Coastguard Worker switch (direction) { 351*970e1046SAndroid Build Coastguard Worker case RED: 352*970e1046SAndroid Build Coastguard Worker return (moment[getIndex(position, cube.g1, cube.b1)] 353*970e1046SAndroid Build Coastguard Worker - moment[getIndex(position, cube.g1, cube.b0)] 354*970e1046SAndroid Build Coastguard Worker - moment[getIndex(position, cube.g0, cube.b1)] 355*970e1046SAndroid Build Coastguard Worker + moment[getIndex(position, cube.g0, cube.b0)]); 356*970e1046SAndroid Build Coastguard Worker case GREEN: 357*970e1046SAndroid Build Coastguard Worker return (moment[getIndex(cube.r1, position, cube.b1)] 358*970e1046SAndroid Build Coastguard Worker - moment[getIndex(cube.r1, position, cube.b0)] 359*970e1046SAndroid Build Coastguard Worker - moment[getIndex(cube.r0, position, cube.b1)] 360*970e1046SAndroid Build Coastguard Worker + moment[getIndex(cube.r0, position, cube.b0)]); 361*970e1046SAndroid Build Coastguard Worker case BLUE: 362*970e1046SAndroid Build Coastguard Worker return (moment[getIndex(cube.r1, cube.g1, position)] 363*970e1046SAndroid Build Coastguard Worker - moment[getIndex(cube.r1, cube.g0, position)] 364*970e1046SAndroid Build Coastguard Worker - moment[getIndex(cube.r0, cube.g1, position)] 365*970e1046SAndroid Build Coastguard Worker + moment[getIndex(cube.r0, cube.g0, position)]); 366*970e1046SAndroid Build Coastguard Worker } 367*970e1046SAndroid Build Coastguard Worker throw new IllegalArgumentException("unexpected direction " + direction); 368*970e1046SAndroid Build Coastguard Worker } 369*970e1046SAndroid Build Coastguard Worker 370*970e1046SAndroid Build Coastguard Worker private static enum Direction { 371*970e1046SAndroid Build Coastguard Worker RED, 372*970e1046SAndroid Build Coastguard Worker GREEN, 373*970e1046SAndroid Build Coastguard Worker BLUE 374*970e1046SAndroid Build Coastguard Worker } 375*970e1046SAndroid Build Coastguard Worker 376*970e1046SAndroid Build Coastguard Worker private static final class MaximizeResult { 377*970e1046SAndroid Build Coastguard Worker // < 0 if cut impossible 378*970e1046SAndroid Build Coastguard Worker int cutLocation; 379*970e1046SAndroid Build Coastguard Worker double maximum; 380*970e1046SAndroid Build Coastguard Worker MaximizeResult(int cut, double max)381*970e1046SAndroid Build Coastguard Worker MaximizeResult(int cut, double max) { 382*970e1046SAndroid Build Coastguard Worker this.cutLocation = cut; 383*970e1046SAndroid Build Coastguard Worker this.maximum = max; 384*970e1046SAndroid Build Coastguard Worker } 385*970e1046SAndroid Build Coastguard Worker } 386*970e1046SAndroid Build Coastguard Worker 387*970e1046SAndroid Build Coastguard Worker private static final class CreateBoxesResult { 388*970e1046SAndroid Build Coastguard Worker int requestedCount; 389*970e1046SAndroid Build Coastguard Worker int resultCount; 390*970e1046SAndroid Build Coastguard Worker CreateBoxesResult(int requestedCount, int resultCount)391*970e1046SAndroid Build Coastguard Worker CreateBoxesResult(int requestedCount, int resultCount) { 392*970e1046SAndroid Build Coastguard Worker this.requestedCount = requestedCount; 393*970e1046SAndroid Build Coastguard Worker this.resultCount = resultCount; 394*970e1046SAndroid Build Coastguard Worker } 395*970e1046SAndroid Build Coastguard Worker } 396*970e1046SAndroid Build Coastguard Worker 397*970e1046SAndroid Build Coastguard Worker private static final class Box { 398*970e1046SAndroid Build Coastguard Worker int r0 = 0; 399*970e1046SAndroid Build Coastguard Worker int r1 = 0; 400*970e1046SAndroid Build Coastguard Worker int g0 = 0; 401*970e1046SAndroid Build Coastguard Worker int g1 = 0; 402*970e1046SAndroid Build Coastguard Worker int b0 = 0; 403*970e1046SAndroid Build Coastguard Worker int b1 = 0; 404*970e1046SAndroid Build Coastguard Worker int vol = 0; 405*970e1046SAndroid Build Coastguard Worker } 406*970e1046SAndroid Build Coastguard Worker } 407