source: trunk/src/org/apollo/gui/TimeAxis.java@ 1102

Last change on this file since 1102 was 1102, checked in by davidb, 6 years ago

Reworking of the code-base to separate logic from graphics. This version of Expeditee now supports a JFX graphics as an alternative to SWING

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