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