source: trunk/src_apollo/org/apollo/gui/TimeAxis.java@ 375

Last change on this file since 375 was 375, checked in by bjn8, 16 years ago

Lots of minor improvements made prior to evluation study, and small things done afterwards for report :)

File size: 10.5 KB
Line 
1package org.apollo.gui;
2
3import java.awt.Color;
4import java.awt.Font;
5import java.awt.FontMetrics;
6import java.awt.GradientPaint;
7import java.awt.Graphics;
8import java.awt.Graphics2D;
9import java.awt.Stroke;
10import java.awt.geom.Rectangle2D;
11import java.awt.image.BufferedImage;
12import java.util.LinkedList;
13
14import org.apollo.ApolloSystem;
15import org.expeditee.gui.Browser;
16
17/**
18 * A model time scale along the X axis that can be rendered.
19 *
20 * @author Brook Novak
21 *
22 */
23public class TimeAxis {
24
25 private static final long serialVersionUID = 1L;
26
27 public static final Font TIME_BAR_FONT = new Font("Arial", Font.PLAIN, 14);
28 private static final Stroke TICK_STROKE = Strokes.SOLID_1;
29
30 private static final Color MAJOR_TICK_COLOR = Color.BLACK;
31 private static final Color MINOR_TICK_COLOR = new Color(0.4f, 0.4f, 0.4f);
32
33 private static final int DESIRED_MAJOR_TICK_PIXEL_SPACING = 100;
34 private static final int MIN_TICK_COUNT = 3; // per between majors
35
36 private String[] majorTickLabels = null;
37 private int[] majorTickXPositions = null;
38 private int[] majorTickLabelXPositions = null;
39 private int[] minorTickXPositions = null;
40 private String alternateLabel = null;
41
42 private int width = 0;
43 private long totalTrackMSLength = 0;
44
45 public TimeAxis() {
46 }
47
48 public void setAxis(
49 long startTimeMS,
50 long timeLengthMS,
51 long timeLengthSamples,
52 long totalTrackMSLength,
53 int width) {
54
55 assert(width > 0);
56 assert(startTimeMS >= 0);
57 assert((startTimeMS + timeLengthMS) <= totalTrackMSLength);
58
59 this.width = width;
60 this.totalTrackMSLength = totalTrackMSLength;
61
62 double majorTickPixelSpacing = DESIRED_MAJOR_TICK_PIXEL_SPACING;
63 double majorTickCount = width / majorTickPixelSpacing;
64 double timeAggregation = timeLengthMS / majorTickCount;
65
66 if (timeAggregation <= 0) { // in between ms
67 majorTickLabels = null;
68 majorTickXPositions = null;
69 majorTickLabelXPositions = null;
70 minorTickXPositions = null;
71 alternateLabel = (timeLengthSamples >= 1) ?
72 timeLengthSamples + " samples"
73 : " ";
74 return;
75 }
76
77 // Re-adjust majorTickCount and aggregationSize to nice values
78 // update majorTickPixelSpacing.
79
80 // so users can follow the axis more easily
81 boolean agregationAdjusted = true;
82
83 if (timeAggregation > ((5 * 60000) + 30000)) {
84 agregationAdjusted = false;
85 } else if (timeAggregation > ((4 * 60000) + 30000)) { // 4:30 - 5:30
86 timeAggregation = 5 * 60000;
87 } else if (timeAggregation > ((3 * 60000) + 30000)) { // 3:30 - 4:30
88 timeAggregation = 4 * 60000;
89 } else if (timeAggregation > ((2 * 60000) + 30000)) { // 2:30 - 3:30
90 timeAggregation = 3 * 60000;
91 } else if (timeAggregation > (60000 + 30000)) { // 1:30 - 2:30
92 timeAggregation = 2 * 60000;
93 } else if (timeAggregation > 40000) { // 0:40 - 1:30
94 timeAggregation = 60000;
95 } else if (timeAggregation > 25000) { // 0:25 - 0:40
96 timeAggregation = 30000;
97 } else if (timeAggregation > 15000) { // 0:15 - 0:25
98 timeAggregation = 20000;
99 } else if (timeAggregation > 9000) { // 0:9 - 0:15
100 timeAggregation = 10000;
101 } else if (timeAggregation > 6500) { // 0:6.5 - 0:9
102 timeAggregation = 8000;
103 } else if (timeAggregation > 4500) { // 4.5s - 6.5s
104 timeAggregation = 5000;
105 } else if (timeAggregation > 3500) { // 3.5s - 4.5s
106 timeAggregation = 4000;
107 } else if (timeAggregation > 2500) { // 2.5s - 3.5s
108 timeAggregation = 3000;
109 } else if (timeAggregation > 1500) { // 1.5s - 2.5s
110 timeAggregation = 2000;
111 } else if (timeAggregation > 850) { // 850ms - 1.5s
112 timeAggregation = 1000;
113 } else if (timeAggregation > 600) { // 600ms - 850ms
114 timeAggregation = 750;
115 } else if (timeAggregation > 380) { // 380ms - 600ms
116 timeAggregation = 500;
117 } else if (timeAggregation > 120) { // 120ms - 380ms
118 timeAggregation = 250;
119 } else if (timeAggregation > 50) { // 50ms - 120ms
120 timeAggregation = 100;
121 } else {
122 agregationAdjusted = false;
123 }
124
125 if (agregationAdjusted) {
126 majorTickCount = timeLengthMS / timeAggregation;
127 majorTickPixelSpacing = width / majorTickCount;
128 }
129
130 // The axis starts at zero always... calc the offset for the first tick
131
132 double currentTickTime = startTimeMS - (startTimeMS % timeAggregation);
133 if (currentTickTime < startTimeMS) {
134 currentTickTime += timeAggregation;
135 }
136
137
138 double offsetPortion = ((double)(startTimeMS % timeAggregation) / (double)timeAggregation);
139 if (offsetPortion > 0) offsetPortion = 1.0 - offsetPortion;
140 double majorTickXStart = offsetPortion * majorTickPixelSpacing;
141 double currentTickX = majorTickXStart;
142
143 // Create major x ticks
144 LinkedList<Integer> majorTickPositions = new LinkedList<Integer>();
145 LinkedList<String> majorTickLabels = new LinkedList<String>();
146 LinkedList<Integer> majorTickLabelPositions = new LinkedList<Integer>();
147
148 Graphics g = null;
149 try {
150
151
152 // Create a temp graphics for centered label positioning
153 if (Browser._theBrowser != null && Browser._theBrowser.g != null) {
154 g = Browser._theBrowser.g.create();
155 } else {
156 g = new BufferedImage(1,1,BufferedImage.TYPE_BYTE_INDEXED).getGraphics();
157 }
158
159 do {
160
161 majorTickPositions.add(new Integer((int)currentTickX));
162
163 String label = createSpecificTimeLabel((long)currentTickTime, timeAggregation);
164
165 majorTickLabels.add(label);
166
167 // Center label
168 FontMetrics fm = g.getFontMetrics(TIME_BAR_FONT);
169 Rectangle2D rect = fm.getStringBounds(label, g);
170 int textWidth = (int)(rect.getWidth());
171
172 majorTickLabelPositions.add(new Integer((int)currentTickX - (textWidth >> 1)));
173
174 currentTickX += majorTickPixelSpacing;
175 currentTickTime += timeAggregation;
176
177 } while (currentTickX < width);
178
179
180 } finally {
181 if (g != null)
182 g.dispose();
183 }
184
185 this.majorTickLabels = majorTickLabels.toArray(new String[0]);
186
187 majorTickXPositions = new int[majorTickPositions.size()];
188 for (int i = 0; i < majorTickPositions.size(); i++)
189 majorTickXPositions[i] = majorTickPositions.get(i);
190
191 majorTickLabelXPositions = new int[majorTickLabelPositions.size()];
192 for (int i = 0; i < majorTickLabelPositions.size(); i++)
193 majorTickLabelXPositions[i] = majorTickLabelPositions.get(i);
194
195
196 // Build minor ticks
197 double minorTickPixelSpacing = majorTickPixelSpacing / (MIN_TICK_COUNT + 1);
198
199 if (minorTickPixelSpacing > 5) { // have some safety condition - avoid clutter
200
201 // Find minor tick start X
202 double tx = majorTickXStart;
203 while (tx > 0) tx -= minorTickPixelSpacing;
204 tx += minorTickPixelSpacing;
205 if (tx == majorTickXStart) tx += minorTickPixelSpacing;
206
207 LinkedList<Integer> minorPositions = new LinkedList<Integer>();
208
209 // Draw the minor ticks
210 while (tx < width) {
211
212 minorPositions.add(new Integer((int)tx));
213
214 tx += minorTickPixelSpacing;
215
216 // Skip major positions
217 if ((((int)(tx - majorTickXStart)) % (int)majorTickPixelSpacing) <= 4) // could be a pixel out
218 tx += minorTickPixelSpacing;
219
220 }
221
222 minorTickXPositions = new int[minorPositions.size()];
223 for (int i = 0; i < minorTickXPositions.length; i++)
224 minorTickXPositions[i] = minorPositions.get(i).intValue();
225 }
226 }
227
228
229 public String createSpecificTimeLabel(long atTime, double timeAggregation) {
230
231 Long min = null;
232 Long sec = null;
233 Long ms = null;
234
235 if (timeAggregation < 1000) { // omit MS if aggregation size >= 1sec
236
237 ms = new Long((atTime) % 1000);
238
239 }
240
241 if (timeAggregation < 60000 &&
242 totalTrackMSLength >= 1000) { // omit sec if aggregation size >= 1min or track length < 1 sec
243
244 sec = new Long(((atTime) / 1000) % 60);
245
246 }
247
248 if (totalTrackMSLength >= 60000) { // omit min if track length < 1 min
249
250 min = new Long((atTime) / 60000);
251
252 }
253
254 String label = createTimeLabel(min, sec, ms);
255 if (label == null) {
256 label = "00:00:00";
257 }
258
259 return label;
260
261 }
262
263 public static String createTimeLabel(Long min, Long sec, Long ms) {
264
265 if (min == null && sec == null && ms != null) {
266 return ms + "ms";
267 }
268
269 if (min == null && sec != null && ms == null) {
270 return sec + "secs";
271 }
272
273 if (min == null && sec != null && ms != null) {
274 String strms = ms.toString();
275 String strsec = sec.toString();
276
277 while (strms.length() < 3) strms = "0" + strms;
278
279 return strsec + "." + strms + "secs";
280 }
281
282 if (min != null && sec == null && ms == null) {
283 return min + "mins";
284 }
285
286
287 if (min != null && ms != null) {
288
289 if (sec == null) sec = 0L;
290
291 String strms = ms.toString();
292 String strsec = sec.toString();
293 String strmin = min.toString();
294
295 while (strms.length() < 3) strms = "0" + strms;
296 while (strsec.length() < 2) strsec = "0" + strsec;
297
298 return strmin + ":" + strsec + "." + strms;
299 }
300
301 if (min != null && sec != null && ms == null) {
302
303 String strsec = sec.toString();
304 String strmin = min.toString();
305
306 while (strsec.length() < 2) strsec = "0" + strsec;
307
308 return strmin + ":" + strsec;
309
310 }
311
312 return null; // all is null
313
314 }
315
316 public void paint(Graphics g, int x, int y, int height, Color backgroundColor) {
317
318 int majorTickHeight = height / 2;
319 int minorTickHeight = height / 4;
320
321 // Draw backing
322 if (ApolloSystem.useQualityGraphics) {
323
324 GradientPaint gp = new GradientPaint(
325 x + (width / 2), y + (int)(height * 0.5), backgroundColor,
326 x + (width / 2), y, SampledTrackGraphView.DEFAULT_BACKGROUND_HIGHTLIGHTS_COLOR);
327 ((Graphics2D)g).setPaint(gp);
328
329 } else {
330 g.setColor(backgroundColor);
331 }
332
333 g.fillRect(x, y, width, height);
334
335 g.setFont(TIME_BAR_FONT);
336
337 if (majorTickXPositions != null) {
338
339 ((Graphics2D)g).setStroke(TICK_STROKE);
340
341 g.setColor(MAJOR_TICK_COLOR);
342 // Draw Major ticks and labels
343 for (int i = 0; i < majorTickLabels.length; i++) {
344
345 // Label
346 g.drawString(
347 majorTickLabels[i],
348 x + majorTickLabelXPositions[i],
349 y + height - 2);
350
351 // Tick
352 g.drawLine(
353 x + majorTickXPositions[i],
354 y,
355 x + majorTickXPositions[i],
356 y + majorTickHeight);
357
358 }
359
360 g.setColor(MINOR_TICK_COLOR);
361
362 // Draw minor ticks
363 if (minorTickXPositions != null) {
364 for (int tx : minorTickXPositions) {
365 g.drawLine(
366 x + tx,
367 y,
368 x + tx,
369 y + minorTickHeight);
370 }
371 }
372
373 } else if (alternateLabel != null) {
374
375 g.setColor(Color.BLACK);
376
377 FontMetrics fm = g.getFontMetrics(TIME_BAR_FONT);
378 Rectangle2D rect = fm.getStringBounds(alternateLabel, g);
379 int textWidth = (int)(rect.getWidth());
380
381 g.drawString(
382 alternateLabel,
383 x + ((width >> 1) - (textWidth >> 1)),
384 y + (height - 2));
385
386 }
387
388 }
389
390 public int getWidth() {
391 return width;
392 }
393
394}
Note: See TracBrowser for help on using the repository browser.