1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.diyefi.openlogviewer.graphing;
24
25 import java.awt.Color;
26 import java.awt.Graphics;
27 import java.awt.Graphics2D;
28 import java.awt.event.HierarchyBoundsListener;
29 import java.awt.event.HierarchyEvent;
30 import java.beans.PropertyChangeEvent;
31 import java.beans.PropertyChangeListener;
32
33 import javax.swing.JPanel;
34 import org.diyefi.openlogviewer.OpenLogViewer;
35 import org.diyefi.openlogviewer.genericlog.GenericDataElement;
36 import org.diyefi.openlogviewer.utils.MathUtils;
37
38
39
40
41
42
43
44
45
46 public class SingleGraphPanel extends JPanel implements HierarchyBoundsListener, PropertyChangeListener {
47 private static final long serialVersionUID = 1L;
48
49 private static final double GRAPH_TRACE_SIZE_AS_PERCENTAGE_OF_TOTAL_GRAPH_SIZE = 0.95;
50 private GenericDataElement GDE;
51 private double[] dataPointsToDisplay;
52 private double[][] dataPointRangeInfo;
53 private int availableDataRecords;
54 private int graphBeginningIndex;
55 private int graphEndingIndex;
56
57 public SingleGraphPanel() {
58 this.setOpaque(false);
59 this.setLayout(null);
60 this.GDE = null;
61 dataPointsToDisplay = null;
62 dataPointRangeInfo = null;
63 graphBeginningIndex = Integer.MIN_VALUE;
64 graphEndingIndex = Integer.MIN_VALUE;
65 }
66
67 @Override
68 public void ancestorMoved(final HierarchyEvent e) {
69 }
70
71 @Override
72 public final void ancestorResized(final HierarchyEvent e) {
73 if (e.getID() == HierarchyEvent.ANCESTOR_RESIZED) {
74 sizeGraph();
75 }
76 }
77
78 @Override
79 public final void propertyChange(final PropertyChangeEvent evt) {
80 if (evt.getPropertyName().equalsIgnoreCase("Split")) {
81 sizeGraph();
82 }
83 }
84
85 @Override
86 public final void paint(final Graphics g) {
87 boolean zoomedOut = OpenLogViewer.getInstance().getEntireGraphingPanel().isZoomedOutBeyondOneToOne();
88 if (zoomedOut) {
89 initGraphZoomedOut();
90 } else {
91 initGraphZoomed();
92 }
93
94 if (hasDataPointToDisplay()) {
95 paintDataPointsAndTraces(g);
96 }
97 }
98
99 private void paintDataPointsAndTraces(final Graphics g) {
100
101 final Graphics2D g2d = (Graphics2D) g;
102 g2d.setColor(GDE.getDisplayColor());
103
104
105 double leftOfTraceData = -Double.MAX_VALUE;
106 double traceData = -Double.MAX_VALUE;
107 double rightOfTraceData = dataPointsToDisplay[0];
108
109
110 boolean atTraceBeginning = false;
111 boolean insideTrace = false;
112 boolean atTraceEnd = false;
113
114
115 final boolean zoomedOut = OpenLogViewer.getInstance().getEntireGraphingPanel().isZoomedOutBeyondOneToOne();
116 int zoom = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom();
117 if (zoomedOut) {
118 zoom = 1;
119 }
120 final double graphPosition = OpenLogViewer.getInstance().getEntireGraphingPanel().getGraphPosition();
121 final double offset = (graphPosition % 1) * zoom;
122 int screenPositionXCoord = -EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT;
123 if (!zoomedOut) {
124 screenPositionXCoord = -(int) Math.round(offset) - (EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_IN * zoom);
125 }
126 int screenPositionYCoord = Integer.MIN_VALUE;
127 int nextScreenPositionYCoord = getScreenPositionYCoord(rightOfTraceData, GDE.getDisplayMinValue(), GDE.getDisplayMaxValue());
128
129
130 for (int i = 0; i < dataPointsToDisplay.length; i++) {
131
132 if (i > 0) {
133 leftOfTraceData = dataPointsToDisplay[i - 1];
134 } else {
135 leftOfTraceData = -Double.MAX_VALUE;
136 }
137
138 traceData = dataPointsToDisplay[i];
139
140 if (i + 1 < dataPointsToDisplay.length) {
141 rightOfTraceData = dataPointsToDisplay[i + 1];
142 } else {
143 rightOfTraceData = -Double.MAX_VALUE;
144 }
145
146
147 screenPositionYCoord = nextScreenPositionYCoord;
148 nextScreenPositionYCoord = getScreenPositionYCoord(rightOfTraceData, GDE.getDisplayMinValue(), GDE.getDisplayMaxValue());
149
150
151 if (i == graphBeginningIndex || i == graphEndingIndex) {
152 g2d.drawLine(screenPositionXCoord - 2, screenPositionYCoord - 1, screenPositionXCoord - 2, screenPositionYCoord + 1);
153 g2d.drawLine(screenPositionXCoord - 1, screenPositionYCoord + 2, screenPositionXCoord + 1, screenPositionYCoord + 2);
154 g2d.drawLine(screenPositionXCoord - 1, screenPositionYCoord - 2, screenPositionXCoord + 1, screenPositionYCoord - 2);
155 g2d.drawLine(screenPositionXCoord + 2, screenPositionYCoord - 1, screenPositionXCoord + 2, screenPositionYCoord + 1);
156 }
157
158
159 if (leftOfTraceData == -Double.MAX_VALUE && traceData != -Double.MAX_VALUE) {
160
161 atTraceBeginning = true;
162 insideTrace = true;
163 }
164
165 if (traceData != -Double.MAX_VALUE && rightOfTraceData == -Double.MAX_VALUE) {
166
167 atTraceEnd = true;
168 }
169
170
171 if (!zoomedOut && zoom > 5) {
172
173 if (atTraceBeginning) {
174 if (traceData != rightOfTraceData) {
175
176 g2d.fillRect(screenPositionXCoord - 1, screenPositionYCoord - 1, 3, 3);
177 }
178 } else if (atTraceEnd) {
179 if (traceData != leftOfTraceData) {
180
181 g2d.fillRect(screenPositionXCoord - 1, screenPositionYCoord - 1, 3, 3);
182 }
183 } else if (insideTrace) {
184 if (traceData != leftOfTraceData || traceData != rightOfTraceData) {
185
186 g2d.fillRect(screenPositionXCoord - 1, screenPositionYCoord - 1, 3, 3);
187 }
188 }
189 } else if (insideTrace) {
190
191
192 g2d.drawLine(screenPositionXCoord, screenPositionYCoord, screenPositionXCoord, screenPositionYCoord);
193 }
194
195
196 if (insideTrace && !atTraceEnd) {
197 g2d.drawLine(screenPositionXCoord, screenPositionYCoord, screenPositionXCoord + zoom, nextScreenPositionYCoord);
198 }
199
200
201 if (atTraceEnd) {
202 insideTrace = false;
203 atTraceEnd = false;
204 }
205 atTraceBeginning = false;
206
207
208 screenPositionXCoord += zoom;
209 }
210 }
211
212 private int getScreenPositionYCoord(final Double traceData, final double minValue, final double maxValue) {
213 int point = 0;
214 final int height = (int) (this.getHeight() * GRAPH_TRACE_SIZE_AS_PERCENTAGE_OF_TOTAL_GRAPH_SIZE);
215 if (maxValue != minValue) {
216 point = (int) (height - (height * ((traceData - minValue) / (maxValue - minValue))));
217 }
218 return point;
219 }
220
221 private boolean hasDataPointToDisplay() {
222 boolean result = false;
223 if ((dataPointsToDisplay != null) && (dataPointsToDisplay.length > 0)) {
224 result = true;
225 }
226 return result;
227 }
228
229
230
231
232
233 public final void setData(final GenericDataElement GDE) {
234 this.GDE = GDE;
235 this.availableDataRecords = GDE.size() + 1;
236
237 sizeGraph();
238 }
239
240 public final GenericDataElement getData() {
241 return GDE;
242 }
243
244
245
246
247
248
249
250 public final String getMouseInfo(final int cursorPosition) {
251 boolean zoomedOut = OpenLogViewer.getInstance().getEntireGraphingPanel().isZoomedOutBeyondOneToOne();
252 String info = "-.-";
253 if (zoomedOut) {
254 info = getMouseInfoZoomedOut(cursorPosition);
255 } else {
256 info = getMouseInfoZoomed(cursorPosition);
257 }
258
259 return info;
260 }
261
262
263
264
265
266
267
268 private final String getMouseInfoZoomed(final int cursorPosition) {
269 String result = "-.-";
270 final double graphPosition = OpenLogViewer.getInstance().getEntireGraphingPanel().getGraphPosition();
271 final int zoom = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom();
272 final double offset = (graphPosition % 1) * zoom;
273 final int cursorPositionPlusOffset = cursorPosition + (int) offset;
274 double numSnapsFromLeft = ((double) cursorPositionPlusOffset / (double) zoom);
275 numSnapsFromLeft = Math.round(numSnapsFromLeft);
276 final int dataLocation = (int) graphPosition + (int) numSnapsFromLeft;
277 if ((dataLocation >= 0) && (dataLocation < availableDataRecords)) {
278 double data = GDE.get(dataLocation);
279 data = MathUtils.roundToSignificantFigures(data, 6);
280 result = Double.toString(data);
281 if (result.length() > 8) {
282 result = result.substring(0, 8);
283 }
284 }
285 return result;
286 }
287
288
289
290
291
292
293
294 private final String getMouseInfoZoomedOut(int cursorPosition) {
295 String result = "-.- | -.- | -.-";
296 if ((cursorPosition >= 0) && (cursorPosition < dataPointRangeInfo.length)) {
297 double minData = dataPointRangeInfo[cursorPosition + EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT][0];
298 double meanData = dataPointRangeInfo[cursorPosition + EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT][1];
299 double maxData = dataPointRangeInfo[cursorPosition + EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT][2];
300 if (minData != -Double.MAX_VALUE) {
301 minData = MathUtils.roundToSignificantFigures(minData, 6);
302 maxData = MathUtils.roundToSignificantFigures(maxData, 6);
303 String resultMin = Double.toString(minData);
304 String resultMax = Double.toString(maxData);
305 if (resultMin.length() > 8) {
306 resultMin = resultMin.substring(0, 8);
307 }
308 if (resultMax.length() > 8) {
309 resultMax = resultMax.substring(0, 8);
310 }
311 meanData = MathUtils.roundToSignificantFigures(meanData, 6);
312 String resultMean = Double.toString(meanData);
313 if (resultMin.length() > resultMax.length() && resultMin.length() < resultMean.length()) {
314 meanData = MathUtils.roundToSignificantFigures(meanData, resultMin.length() - 2);
315 resultMean = resultMean.substring(0, resultMin.length());
316 } else if (resultMax.length() < resultMean.length()) {
317 meanData = MathUtils.roundToSignificantFigures(meanData, resultMax.length() - 2);
318 resultMean = resultMean.substring(0, resultMax.length());
319 }
320
321 result = resultMin + " | " + resultMean + " | " + resultMax;
322 }
323 }
324 return result;
325 }
326
327 public final Color getColor() {
328 return GDE.getDisplayColor();
329 }
330
331 public final void setColor(final Color c) {
332 GDE.setDisplayColor(c);
333 }
334
335
336
337
338 public final void initGraphZoomed() {
339 if (GDE != null) {
340 final int graphPosition = (int)OpenLogViewer.getInstance().getEntireGraphingPanel().getGraphPosition();
341 int graphWindowWidth = OpenLogViewer.getInstance().getEntireGraphingPanel().getWidth();
342 final int zoom = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom();
343 int numberOfPointsThatFitInDisplay = graphWindowWidth / zoom;
344 numberOfPointsThatFitInDisplay += EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_IN;
345 numberOfPointsThatFitInDisplay += EntireGraphingPanel.RIGHT_OFFSCREEN_POINTS_ZOOMED_IN;
346 dataPointsToDisplay = new double[numberOfPointsThatFitInDisplay];
347 int position = graphPosition - EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_IN;
348
349
350 graphBeginningIndex = Integer.MIN_VALUE;
351 graphEndingIndex = Integer.MIN_VALUE;
352
353
354 for (int i = 0; i < numberOfPointsThatFitInDisplay; i++) {
355 if (position >= 0 && position < availableDataRecords) {
356 dataPointsToDisplay[i] = GDE.get(position);
357
358
359 if (position == 0) {
360 graphBeginningIndex = i;
361 }
362
363 if (position == availableDataRecords - 1) {
364 graphEndingIndex = i;
365 }
366
367 } else {
368 dataPointsToDisplay[i] = -Double.MAX_VALUE;
369 }
370 position++;
371 }
372 }
373 }
374
375
376
377
378 public final void initGraphZoomedOut() {
379 if (GDE != null) {
380 final int graphPosition = (int)OpenLogViewer.getInstance().getEntireGraphingPanel().getGraphPosition();
381 int graphWindowWidth = OpenLogViewer.getInstance().getEntireGraphingPanel().getWidth();
382 final int zoom = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom();
383 final int position = graphPosition - (EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT * zoom);
384 dataPointsToDisplay = new double[graphWindowWidth + EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT + EntireGraphingPanel.RIGHT_OFFSCREEN_POINTS_ZOOMED_OUT];
385 dataPointRangeInfo = new double[dataPointsToDisplay.length][3];
386 final int numberOfRealPointsThatFitInDisplay = (graphWindowWidth * zoom)
387 + (EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT * zoom)
388 + (EntireGraphingPanel.RIGHT_OFFSCREEN_POINTS_ZOOMED_OUT * zoom);
389 final int rightGraphPosition = position + numberOfRealPointsThatFitInDisplay;
390
391
392 graphBeginningIndex = Integer.MIN_VALUE;
393 graphEndingIndex = Integer.MIN_VALUE;
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411 int nextAarrayIndex = 0;
412 double leftOfNewData = GDE.get(0);
413 if (position > 0 && position < availableDataRecords) {
414 leftOfNewData = GDE.get(position);
415 }
416
417 if (zoom < (availableDataRecords / 2)) {
418
419 for (int i = position; i < rightGraphPosition; i += zoom) {
420
421 if (i >= 0 && i < availableDataRecords) {
422 double minData = Double.MAX_VALUE;
423 double maxData = -Double.MAX_VALUE;
424 double newData = 0.0;
425 double acummulateData = 0.0;
426 int divisor = 0;
427
428 for (int j = 0; j < zoom; j++) {
429 int gdeIndex = i + j;
430 if (gdeIndex >= 0 && gdeIndex < availableDataRecords) {
431 newData = GDE.get(gdeIndex);
432 acummulateData += newData;
433 divisor++;
434 if (newData < minData) {
435 minData = newData;
436 }
437
438 if (newData > maxData) {
439 maxData = newData;
440 }
441
442
443 if (graphBeginningIndex == Integer.MIN_VALUE && (gdeIndex >= 0 && gdeIndex < zoom)) {
444 graphBeginningIndex = nextAarrayIndex;
445 }
446
447 if (gdeIndex == availableDataRecords - 1) {
448 graphEndingIndex = nextAarrayIndex;
449 }
450
451 }
452 }
453 double averageData = acummulateData / divisor;
454 if (averageData > leftOfNewData) {
455 dataPointsToDisplay[nextAarrayIndex] = maxData;
456 leftOfNewData = maxData;
457 } else if (averageData < leftOfNewData) {
458 dataPointsToDisplay[nextAarrayIndex] = minData;
459 leftOfNewData = minData;
460 } else {
461 dataPointsToDisplay[nextAarrayIndex] = averageData;
462 leftOfNewData = averageData;
463 }
464 dataPointRangeInfo[nextAarrayIndex][0] = minData;
465 dataPointRangeInfo[nextAarrayIndex][1] = averageData;
466 dataPointRangeInfo[nextAarrayIndex][2] = maxData;
467 nextAarrayIndex++;
468 } else {
469 dataPointsToDisplay[nextAarrayIndex] = -Double.MAX_VALUE;
470 dataPointRangeInfo[nextAarrayIndex][0] = -Double.MAX_VALUE;
471 dataPointRangeInfo[nextAarrayIndex][1] = -Double.MAX_VALUE;
472 dataPointRangeInfo[nextAarrayIndex][2] = -Double.MAX_VALUE;
473 nextAarrayIndex++;
474 }
475 }
476 } else {
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491 for (int i = position; i < 0; i += zoom) {
492 dataPointsToDisplay[nextAarrayIndex] = -Double.MAX_VALUE;
493 dataPointRangeInfo[nextAarrayIndex][0] = -Double.MAX_VALUE;
494 dataPointRangeInfo[nextAarrayIndex][1] = -Double.MAX_VALUE;
495 dataPointRangeInfo[nextAarrayIndex][2] = -Double.MAX_VALUE;
496 nextAarrayIndex++;
497 }
498
499
500 double minData = Double.MAX_VALUE;
501 double maxData = -Double.MAX_VALUE;
502 double newData = 0.0;
503 double acummulateData = 0.0;
504 int divisor = 0;
505 for (int i = 0; i < availableDataRecords; i++) {
506 newData = GDE.get(i);
507 acummulateData += newData;
508 divisor++;
509 if (newData < minData) {
510 minData = newData;
511 }
512
513 if (newData > maxData) {
514 maxData = newData;
515 }
516 }
517 double averageData = acummulateData / divisor;
518 dataPointsToDisplay[nextAarrayIndex] = averageData;
519 dataPointRangeInfo[nextAarrayIndex][0] = minData;
520 dataPointRangeInfo[nextAarrayIndex][1] = averageData;
521 dataPointRangeInfo[nextAarrayIndex][2] = maxData;
522
523
524 graphBeginningIndex = nextAarrayIndex;
525 graphEndingIndex = nextAarrayIndex;
526
527 nextAarrayIndex++;
528
529
530 for (int i = zoom * 2; i < rightGraphPosition; i += zoom) {
531 dataPointsToDisplay[nextAarrayIndex] = -Double.MAX_VALUE;
532 dataPointRangeInfo[nextAarrayIndex][0] = -Double.MAX_VALUE;
533 dataPointRangeInfo[nextAarrayIndex][1] = -Double.MAX_VALUE;
534 dataPointRangeInfo[nextAarrayIndex][2] = -Double.MAX_VALUE;
535 nextAarrayIndex++;
536 }
537 }
538 }
539 }
540
541
542
543
544 public final void sizeGraph() {
545 final MultiGraphLayeredPane lg = OpenLogViewer.getInstance().getMultiGraphLayeredPane();
546 int wherePixel = 0;
547 if (lg.getTotalSplits() > 1) {
548 if (GDE.getSplitNumber() <= lg.getTotalSplits()) {
549 wherePixel += lg.getHeight() / lg.getTotalSplits() * GDE.getSplitNumber() - (lg.getHeight() / lg.getTotalSplits());
550 } else {
551 wherePixel += lg.getHeight() / lg.getTotalSplits() * lg.getTotalSplits() - (lg.getHeight() / lg.getTotalSplits());
552 }
553 }
554
555 this.setBounds(0, wherePixel, lg.getWidth(), lg.getHeight() / (lg.getTotalSplits()));
556 final boolean zoomedOut = OpenLogViewer.getInstance().getEntireGraphingPanel().isZoomedOutBeyondOneToOne();
557 if (zoomedOut) {
558 initGraphZoomedOut();
559 } else {
560 initGraphZoomed();
561 }
562 }
563
564
565
566
567
568 public final int graphSize() {
569 return availableDataRecords;
570 }
571 }