001 /* 002 * MedianCutNode 003 * 004 * Copyright (c) 2001, 2002, 2003 Marco Schmidt. 005 * All rights reserved. 006 */ 007 008 package net.sourceforge.jiu.color.quantization; 009 010 import net.sourceforge.jiu.color.quantization.RGBColor; 011 import net.sourceforge.jiu.data.RGBIndex; 012 013 /** 014 * An instance of this node class represents a cuboid part 015 * of the color cube representing the three-dimensional RGB color space. 016 * @author Marco Schmidt 017 * @see MedianCutQuantizer 018 */ 019 public class MedianCutNode implements RGBIndex 020 { 021 private int axis; 022 private boolean axisDetermined; 023 private int diff; 024 private int index1; 025 private int index2; 026 private MedianCutNode leftSuccessor; 027 private int[] max; 028 private int medianValue; 029 private int middleIndex; 030 private int[] min; 031 private int paletteIndex; 032 private MedianCutNode parent; 033 private int[] reprColor; 034 private MedianCutNode rightSuccessor; 035 036 /** 037 * Creates a node for a Median Cut tree of nodes with index values for 038 * some external color array and the parent node. 039 * This parent is null for the root node. 040 * @param parent the parent node of this new node, should be null only for the root node 041 * @param index1 the index value of the first element of colors in the color list 042 * @param index2 the index value of the last element of colors in the color list; must be larger than or equal to index1 043 * @throws IllegalArgumentException if index1 is larger than index2 044 */ 045 public MedianCutNode(MedianCutNode parent, int index1, int index2) 046 { 047 if (index1 > index2) 048 { 049 throw new IllegalArgumentException("MedianCutNode constructor, index1 must be smaller than or equal to index2."); 050 } 051 this.parent = parent; 052 this.index1 = index1; 053 this.index2 = index2; 054 determineMiddleIndex(); 055 leftSuccessor = null; 056 rightSuccessor = null; 057 diff = 0; 058 axisDetermined = false; 059 max = new int[3]; 060 min = new int[3]; 061 paletteIndex = -1; 062 } 063 064 /** 065 * Returns if this node can be split into two. 066 * This is true if and only if this is a leaf and if the color 067 * list index values represent an interval of at least length 2. 068 * @return if this node can be split into two nodes 069 */ 070 public boolean canBeSplit() 071 { 072 return isLeaf() && index1 != index2; 073 } 074 075 /** 076 * Computes the distance in RGB color space between the representative color of this node and the 077 * argument node and returns it as non-negative value. 078 */ 079 public double computeRgbDistance(MedianCutNode node) 080 { 081 int[] c = node.reprColor; 082 return RGBColor.computeDistance(reprColor[INDEX_RED], reprColor[INDEX_GREEN], reprColor[INDEX_BLUE], c[INDEX_RED], c[INDEX_GREEN], c[INDEX_BLUE]); 083 } 084 085 /** 086 * Computes the middle index value of this node. 087 * It uses the index values given to this node's constructor, index1 and index2. 088 */ 089 private void determineMiddleIndex() 090 { 091 if (index1 == index2) 092 { 093 middleIndex = index1; 094 } 095 else 096 { 097 middleIndex = (index1 + index2) / 2; 098 } 099 } 100 101 /** 102 * Returns the axis of the channel whose samples are most widely 103 * distributed among the colors that belong to this node. 104 * @return index of axis, one of the {@link RGBIndex} constants 105 * @throws IllegalArgumentException if that axis has not been determined 106 */ 107 public int getAxisOfLargestDistribution() 108 { 109 if (axisDetermined) 110 { 111 return axis; 112 } 113 else 114 { 115 throw new IllegalArgumentException("The axis has not been determined and can thus not be returned."); 116 } 117 } 118 119 public int getDifferenceOfLargestDistribution() 120 { 121 if (axisDetermined) 122 { 123 return diff; 124 } 125 else 126 { 127 throw new IllegalArgumentException("The axis has not been determined and can thus not be returned."); 128 } 129 } 130 131 public int getLeftIndex() 132 { 133 return index1; 134 } 135 136 /** 137 * Returns left successor node (or null if this node is a leaf). 138 */ 139 public MedianCutNode getLeftSuccessor() 140 { 141 return leftSuccessor; 142 } 143 144 public int getMaxColorSample(int index) 145 { 146 return max[index]; 147 } 148 149 public int getMedianValue() 150 { 151 return medianValue; 152 } 153 154 public int getMiddleIndex() 155 { 156 return middleIndex; 157 } 158 159 public int getMinColorSample(int index) 160 { 161 return min[index]; 162 } 163 164 public int getNumColors() 165 { 166 return index2 - index1 + 1; 167 } 168 169 public int getPaletteIndex() 170 { 171 return paletteIndex; 172 } 173 174 /** 175 * Returns parent node (or null if this node is the root node). 176 */ 177 public MedianCutNode getParentNode() 178 { 179 return parent; 180 } 181 182 public int[] getRepresentativeColor() 183 { 184 return reprColor; 185 } 186 187 public int getRightIndex() 188 { 189 return index2; 190 } 191 192 /** 193 * Returns right successor node (or null if this node is a leaf). 194 */ 195 public MedianCutNode getRightSuccessor() 196 { 197 return rightSuccessor; 198 } 199 200 public MedianCutNode getSuccessor(int[] rgb) 201 { 202 if (rgb[axis] <= medianValue) 203 { 204 return leftSuccessor; 205 } 206 else 207 { 208 return rightSuccessor; 209 } 210 } 211 212 public boolean isAxisDetermined() 213 { 214 return axisDetermined; 215 } 216 217 /** 218 * Returns if this node is a leaf by checking if both successors are null. 219 * Note that the case of one successor being null and the other non-null 220 * should never happen. 221 * @return if this node is a leaf (true) 222 */ 223 public boolean isLeaf() 224 { 225 return (leftSuccessor == null && rightSuccessor == null); 226 } 227 228 public void setLargestDistribution(int newAxis, int newDifference) 229 { 230 if (newAxis != INDEX_RED && newAxis != INDEX_GREEN && newAxis != INDEX_BLUE) 231 { 232 throw new IllegalArgumentException("Axis must be either INDEX_RED, INDEX_GREEN or INDEX_BLUE."); 233 } 234 axis = newAxis; 235 diff = newDifference; 236 axisDetermined = true; 237 } 238 239 public void setMaxColor(int red, int green, int blue) 240 { 241 max[INDEX_RED] = red; 242 max[INDEX_GREEN] = green; 243 max[INDEX_BLUE] = blue; 244 } 245 246 public void setMaxColorSample(int index, int value) 247 { 248 max[index] = value; 249 } 250 251 public void setMedianValue(int newMedianValue) 252 { 253 medianValue = newMedianValue; 254 } 255 256 public void setMinColor(int red, int green, int blue) 257 { 258 min[INDEX_RED] = red; 259 min[INDEX_GREEN] = green; 260 min[INDEX_BLUE] = blue; 261 } 262 263 public void setMinColorSample(int index, int value) 264 { 265 min[index] = value; 266 } 267 268 public void setPaletteIndex(int newPaletteIndex) 269 { 270 paletteIndex = newPaletteIndex; 271 } 272 273 public void setRepresentativeColor(int[] aRepresentativeColor) 274 { 275 if (aRepresentativeColor != null && aRepresentativeColor.length != 3) 276 { 277 throw new IllegalArgumentException("Representative color array argument must have a length of 3."); 278 } 279 reprColor = aRepresentativeColor; 280 } 281 282 /** 283 * Sets the successor nodes for this node. 284 * The successors must be either both null or both initialized. 285 * They must not be equal. 286 * @param left the left successor node 287 * @param right the left successor node 288 */ 289 public void setSuccessors(MedianCutNode left, MedianCutNode right) 290 { 291 if ((left == null && right != null) || 292 (left != null && right == null)) 293 { 294 throw new IllegalArgumentException("The successor nodes must be either both null or both initialized ."); 295 } 296 if (left != null && left == right) 297 { 298 throw new IllegalArgumentException("The successor nodes must not be the same."); 299 } 300 leftSuccessor = left; 301 rightSuccessor = right; 302 } 303 }