1 package org.opentrafficsim.graphs;
2
3 import java.awt.Color;
4 import java.awt.Paint;
5 import java.io.Serializable;
6 import java.util.Arrays;
7
8 import org.jfree.chart.renderer.PaintScale;
9 import org.opentrafficsim.simulationengine.OTSSimulationException;
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 public class ContinuousColorPaintScale implements PaintScale, Serializable
25 {
26
27 private static final long serialVersionUID = 20140000L;
28
29
30 private double[] bounds;
31
32
33 private Color[] boundColors;
34
35
36 private final String format;
37
38
39
40
41
42
43
44
45 ContinuousColorPaintScale(final String format, final double[] bounds, final Color[] boundColors)
46 throws OTSSimulationException
47 {
48 this.format = format;
49 if (bounds.length < 2)
50 {
51 throw new OTSSimulationException("bounds must have >= 2 entries");
52 }
53 if (bounds.length != boundColors.length)
54 {
55 throw new OTSSimulationException("bounds must have same length as boundColors");
56 }
57 this.bounds = new double[bounds.length];
58 this.boundColors = new Color[bounds.length];
59
60
61 for (int nextBound = 0; nextBound < bounds.length; nextBound++)
62 {
63
64 double currentLowest = Double.POSITIVE_INFINITY;
65 int bestIndex = -1;
66 int index;
67 for (index = 0; index < bounds.length; index++)
68 {
69 if (bounds[index] < currentLowest && (nextBound == 0 || bounds[index] > this.bounds[nextBound - 1]))
70 {
71 bestIndex = index;
72 currentLowest = bounds[index];
73 }
74 }
75 if (bestIndex < 0)
76 {
77 throw new OTSSimulationException("duplicate value in bounds");
78 }
79 this.bounds[nextBound] = bounds[bestIndex];
80 this.boundColors[nextBound] = boundColors[bestIndex];
81 }
82 }
83
84
85 @Override
86 public final double getLowerBound()
87 {
88 return this.bounds[0];
89 }
90
91
92
93
94
95
96
97
98
99
100 private static int mixComponent(final double ratio, final int low, final int high)
101 {
102 final double mix = low * (1 - ratio) + high * ratio;
103 int result = (int) mix;
104 if (result < 0)
105 {
106 result = 0;
107 }
108 if (result > 255)
109 {
110 result = 255;
111 }
112 return result;
113 }
114
115
116 @Override
117 public final Paint getPaint(final double value)
118 {
119 int bucket;
120 for (bucket = 0; bucket < this.bounds.length - 1; bucket++)
121 {
122 if (value < this.bounds[bucket + 1])
123 {
124 break;
125 }
126 }
127 if (bucket >= this.bounds.length - 1)
128 {
129 bucket = this.bounds.length - 2;
130 }
131 final double ratio = (value - this.bounds[bucket]) / (this.bounds[bucket + 1] - this.bounds[bucket]);
132 Color mix = new Color(mixComponent(ratio, this.boundColors[bucket].getRed(), this.boundColors[bucket + 1].getRed()),
133 mixComponent(ratio, this.boundColors[bucket].getGreen(), this.boundColors[bucket + 1].getGreen()),
134 mixComponent(ratio, this.boundColors[bucket].getBlue(), this.boundColors[bucket + 1].getBlue()));
135 return mix;
136 }
137
138
139 @Override
140 public final double getUpperBound()
141 {
142 return this.bounds[this.bounds.length - 1];
143 }
144
145
146
147
148
149 public final String getFormat()
150 {
151 return this.format;
152 }
153
154
155 @Override
156 public final String toString()
157 {
158 return "ContinuousColorPaintScale [bounds=" + Arrays.toString(this.bounds) + ", boundColors="
159 + Arrays.toString(this.boundColors) + "]";
160 }
161
162 }