1 package org.opentrafficsim.draw;
2
3 import java.awt.Color;
4 import java.io.Serializable;
5 import java.util.Arrays;
6
7 import org.djutils.exceptions.Throw;
8 import org.djutils.logger.CategoryLogger;
9
10
11
12
13
14
15
16
17
18
19
20 public class BoundsPaintScale implements ColorPaintScale, Serializable
21 {
22
23
24 public static final Color[] GREEN_RED = new Color[] {Color.GREEN, Color.YELLOW, Color.RED};
25
26
27 public static final Color[] GREEN_RED_DARK =
28 new Color[] {Color.GREEN.darker(), Color.GREEN, Color.YELLOW, Color.RED, Color.RED.darker()};
29
30
31 private static final long serialVersionUID = 20181008L;
32
33
34 private double[] bounds;
35
36
37 private Color[] boundColors;
38
39
40
41
42
43
44
45 public BoundsPaintScale(final double[] bounds, final Color[] boundColors) throws IllegalArgumentException
46 {
47 Throw.when(bounds.length < 2, IllegalArgumentException.class, "bounds must have >= 2 entries");
48 Throw.when(bounds.length != boundColors.length, IllegalArgumentException.class,
49 "bounds must have same length as boundColors");
50 this.bounds = new double[bounds.length];
51 this.boundColors = new Color[bounds.length];
52
53
54 for (int nextBound = 0; nextBound < bounds.length; nextBound++)
55 {
56
57 double currentLowest = Double.POSITIVE_INFINITY;
58 int bestIndex = -1;
59 int index;
60 for (index = 0; index < bounds.length; index++)
61 {
62 if (bounds[index] < currentLowest && (nextBound == 0 || bounds[index] > this.bounds[nextBound - 1]))
63 {
64 bestIndex = index;
65 currentLowest = bounds[index];
66 }
67 }
68 Throw.when(bestIndex < 0, IllegalArgumentException.class, "duplicate value in bounds");
69 this.bounds[nextBound] = bounds[bestIndex];
70 this.boundColors[nextBound] = boundColors[bestIndex];
71 }
72 }
73
74
75
76
77
78
79 public static Color[] reverse(final Color[] colors)
80 {
81 Color[] out = new Color[colors.length];
82 for (int i = 0; i < colors.length; i++)
83 {
84 out[colors.length - i - 1] = colors[i];
85 }
86 return out;
87 }
88
89
90
91
92
93
94 public static Color[] hue(final int n)
95 {
96 Color[] out = new Color[n];
97 for (int i = 0; i < n; i++)
98 {
99 out[i] = new Color(Color.HSBtoRGB(((float) i) / n, 1.0f, 1.0f));
100 }
101 return out;
102 }
103
104
105 @Override
106 public Color getPaint(final double value)
107 {
108 if (Double.isNaN(value))
109 {
110 return Color.BLACK;
111 }
112 if (value < this.bounds[0])
113 {
114 return this.boundColors[0];
115 }
116 if (value > this.bounds[this.bounds.length - 1])
117 {
118 return this.boundColors[this.bounds.length - 1];
119 }
120 int index;
121 for (index = 0; index < this.bounds.length - 1; index++)
122 {
123 if (value < this.bounds[index + 1])
124 {
125 break;
126 }
127 }
128 final double ratio;
129 if (index >= this.bounds.length - 1)
130 {
131 index = this.bounds.length - 2;
132 ratio = 1.0;
133 }
134 else
135 {
136 ratio = (value - this.bounds[index]) / (this.bounds[index + 1] - this.bounds[index]);
137 }
138 if (Double.isInfinite(ratio))
139 {
140 CategoryLogger.always().error("Interpolation value for color is infinite based on {} in {} obtaining index {}.",
141 value, this.bounds, index);
142 }
143 Color mix = ColorInterpolator.interpolateColor(this.boundColors[index], this.boundColors[index + 1], ratio);
144 return mix;
145 }
146
147
148 @Override
149 public final double getLowerBound()
150 {
151 return this.bounds[0];
152 }
153
154
155 @Override
156 public final double getUpperBound()
157 {
158 return this.bounds[this.bounds.length - 1];
159 }
160
161
162 @Override
163 public String toString()
164 {
165 return "BoundsPaintScale [bounds=" + Arrays.toString(this.bounds) + ", boundColors=" + Arrays.toString(this.boundColors)
166 + "]";
167 }
168
169 }