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