001package com.hfg.graphics; 002 003import com.hfg.html.HTMLTag; 004import com.hfg.html.Span; 005import com.hfg.html.attribute.HTMLColor; 006import com.hfg.math.Range; 007import com.hfg.util.CompareUtil; 008import com.hfg.util.StringUtil; 009import com.hfg.util.Value; 010import com.hfg.util.ValueImpl; 011import com.hfg.xml.XMLName; 012import com.hfg.xml.XMLTag; 013 014 015//------------------------------------------------------------------------------ 016 017/** 018 Colorist implementation that uses ranges. 019 <div> 020 @author J. Alex Taylor, hairyfatguy.com 021 </div> 022 */ 023//------------------------------------------------------------------------------ 024// com.hfg Library 025// 026// This library is free software; you can redistribute it and/or 027// modify it under the terms of the GNU Lesser General Public 028// License as published by the Free Software Foundation; either 029// version 2.1 of the License, or (at your option) any later version. 030// 031// This library is distributed in the hope that it will be useful, 032// but WITHOUT ANY WARRANTY; without even the implied warranty of 033// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 034// Lesser General Public License for more details. 035// 036// You should have received a copy of the GNU Lesser General Public 037// License along with this library; if not, write to the Free Software 038// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 039// 040// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com 041// jataylor@hairyfatguy.com 042//------------------------------------------------------------------------------ 043public class ScaleColorist<T extends Number> implements Colorist 044{ 045 private ColorScale mColorScale; 046 private Range<T> mRange; 047 048 private static final XMLName XML_RANGE = new XMLName("Range"); 049 private static final XMLName XML_TYPE_ATT = new XMLName("type"); 050 private static final XMLName XML_START_ATT = new XMLName("start"); 051 private static final XMLName XML_END_ATT = new XMLName("end"); 052 053 //########################################################################### 054 // CONSTRUCTORS 055 //########################################################################### 056 057 //--------------------------------------------------------------------------- 058 public ScaleColorist(ColorScale inColorScale, Range<T> inRange) 059 { 060 mColorScale = inColorScale; 061 mRange = inRange; 062 } 063 064 065 //--------------------------------------------------------------------------- 066 public ScaleColorist(XMLTag inXMLTag) 067 { 068 inXMLTag.verifyTagName(XML_COLORIST); 069 070 XMLTag colorScaleTag = inXMLTag.getRequiredSubtagByName(ColorScale.XML_COLOR_SCALE); 071 mColorScale = new ColorScale(colorScaleTag); 072 073 XMLTag rangeTag = inXMLTag.getRequiredSubtagByName(XML_RANGE); 074 mRange = extractRange(rangeTag); 075 } 076 077 //--------------------------------------------------------------------------- 078 private Range extractRange(XMLTag inRangeTag) 079 { 080 Range range = null; 081 082 String type = inRangeTag.getAttributeValue(XML_TYPE_ATT); 083 084 if (type.equals(Integer.class.getSimpleName())) 085 { 086 range = new Range<Integer>(); 087 088 String start = inRangeTag.getAttributeValue(XML_START_ATT); 089 if (StringUtil.isSet(start)) 090 { 091 range.setStart(Integer.parseInt(start)); 092 } 093 094 String end = inRangeTag.getAttributeValue(XML_END_ATT); 095 if (StringUtil.isSet(end)) 096 { 097 range.setEnd(Integer.parseInt(end)); 098 } 099 } 100 else if (type.equals(Long.class.getSimpleName())) 101 { 102 range = new Range<Long>(); 103 104 String start = inRangeTag.getAttributeValue(XML_START_ATT); 105 if (StringUtil.isSet(start)) 106 { 107 range.setStart(Long.parseLong(start)); 108 } 109 110 String end = inRangeTag.getAttributeValue(XML_END_ATT); 111 if (StringUtil.isSet(end)) 112 { 113 range.setEnd(Long.parseLong(end)); 114 } 115 } 116 else if (type.equals(Float.class.getSimpleName())) 117 { 118 range = new Range<Float>(); 119 120 String start = inRangeTag.getAttributeValue(XML_START_ATT); 121 if (StringUtil.isSet(start)) 122 { 123 range.setStart(Float.parseFloat(start)); 124 } 125 126 String end = inRangeTag.getAttributeValue(XML_END_ATT); 127 if (StringUtil.isSet(end)) 128 { 129 range.setEnd(Float.parseFloat(end)); 130 } 131 } 132 else // Use Double as the catch-all 133 { 134 range = new Range<Double>(); 135 136 String start = inRangeTag.getAttributeValue(XML_START_ATT); 137 if (StringUtil.isSet(start)) 138 { 139 range.setStart(Double.parseDouble(start)); 140 } 141 142 String end = inRangeTag.getAttributeValue(XML_END_ATT); 143 if (StringUtil.isSet(end)) 144 { 145 range.setEnd(Double.parseDouble(end)); 146 } 147 } 148 149 return range; 150 } 151 152 //########################################################################### 153 // PUBLIC METHODS 154 //########################################################################### 155 156 //--------------------------------------------------------------------------- 157 public ColorScale getColorScale() 158 { 159 return mColorScale; 160 } 161 162 //--------------------------------------------------------------------------- 163 public Range<T> getRange() 164 { 165 return mRange; 166 } 167 168 169 //--------------------------------------------------------------------------- 170 public XMLTag toXMLTag() 171 { 172 XMLTag tag = new XMLTag(XML_COLORIST); 173 tag.setAttribute(XML_CLASS_ATT, getClass().getName()); 174 175 tag.addSubtag(mColorScale.toXMLTag()); 176 177 XMLTag rangeTag = new XMLTag(XML_RANGE); 178 tag.addSubtag(rangeTag); 179 180 Class typeClass = null; 181 182 if (getRange().getStart() != null) 183 { 184 rangeTag.setAttribute(XML_START_ATT, getRange().getStart()); 185 typeClass = getRange().getStart().getClass(); 186 } 187 188 if (getRange().getEnd() != null) 189 { 190 rangeTag.setAttribute(XML_END_ATT, getRange().getEnd()); 191 typeClass = getRange().getEnd().getClass(); 192 } 193 194 if (typeClass != null) 195 { 196 rangeTag.setAttribute(XML_TYPE_ATT, typeClass.getSimpleName()); 197 } 198 199 return tag; 200 } 201 202 //--------------------------------------------------------------------------- 203 @Override 204 public HTMLTag toDisplayHTML() 205 { 206 // background-image: linear-gradient(315deg, #ff000066,#ffa50066,#ffff0066,#00800066,#0000ff66,#4b008266,#ee82ee66) 207 Span span = new Span() 208 .addStyle("background-image: linear-gradient(90deg, " + StringUtil.join(getColorScale().getColors(), ",") + ");"); 209 210 span.addContent(getRange().getStart() + ""); 211 span.addSpan(getRange().getEnd() + "").addStyle("margin-left:200px;"); 212 213 return span; 214 } 215 216 //--------------------------------------------------------------------------- 217 @Override 218 public int hashCode() 219 { 220 int hashcode = 1; 221 222 if (mRange != null) 223 { 224 hashcode += 31 * mRange.hashCode(); 225 } 226 227 if (mColorScale != null) 228 { 229 hashcode += 31 * mColorScale.hashCode(); 230 } 231 232 return hashcode; 233 } 234 235 //--------------------------------------------------------------------------- 236 @Override 237 public boolean equals(Object inObj2) 238 { 239 return (inObj2 != null 240 && inObj2 instanceof ScaleColorist 241 && 0 == compareTo((ScaleColorist) inObj2)); 242 } 243 244 //--------------------------------------------------------------------------- 245 @Override 246 public int compareTo(Colorist inObj2) 247 { 248 int result = -1; 249 250 if (this == inObj2) 251 { 252 result = 0; 253 } 254 else if (inObj2 instanceof ScaleColorist) 255 { 256 if (inObj2 != null) 257 { 258 result = CompareUtil.compare(mRange, ((ScaleColorist)inObj2).mRange); 259 260 if(0 == result) 261 { 262 result = CompareUtil.compare(mColorScale, ((ScaleColorist)inObj2).mColorScale); 263 } 264 } 265 } 266 267 return result; 268 } 269 270 //--------------------------------------------------------------------------- 271 @Override 272 public ColorSpec colorByNumber(Value inValue) 273 { 274 HTMLColor bgColor = new HTMLColor(mColorScale.assignColorForValue(normalizeValue(inValue).floatValue())); 275 276 HTMLColor fgColor = bgColor.getContrastingColor(); 277 278 return new ColorSpec().setBackgroundColor(bgColor) 279 .setForegroundColor(fgColor); 280 } 281 282 //--------------------------------------------------------------------------- 283 @Override 284 public ColorSpec colorByNumber(Number inValue) 285 { 286 return colorByNumber(new ValueImpl(inValue)); 287 } 288 289 290 //########################################################################### 291 // PRIVATE METHODS 292 //########################################################################### 293 294 //--------------------------------------------------------------------------- 295 private Value normalizeValue(Value inValue) 296 { 297 Value normalizedValue; 298 299 if(inValue.doubleValue() != null) 300 { 301 if(! mRange.contains(inValue.doubleValue())) 302 { 303 throw new RuntimeException("Specified value: " + inValue + " is not within expected range: " + mRange + "!"); 304 } 305 306 normalizedValue = new ValueImpl((inValue.doubleValue() - mRange.getStart().doubleValue()) / mRange.length()); 307 } 308 else 309 { 310 //default to bottom of range if inValue is not a number or is null 311 normalizedValue = new ValueImpl(0); 312 } 313 314 return normalizedValue; 315 } 316 317}