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