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.FontMetrics;
27 import java.awt.Graphics;
28 import java.awt.Graphics2D;
29 import java.math.BigDecimal;
30 import java.math.MathContext;
31
32 import javax.swing.JPanel;
33
34 import org.diyefi.openlogviewer.OpenLogViewer;
35 import org.diyefi.openlogviewer.genericlog.GenericLog;
36
37 public class GraphPositionPanel extends JPanel {
38 private static final long serialVersionUID = 1L;
39
40 private GenericLog genLog;
41 private Color majorGraduationColor;
42 private Color positionDataColor;
43 private Color backgroundColor;
44 private boolean[] validSnappingPositions;
45 private double[] graduationSpacingMultiplier;
46 private double majorGraduationSpacing;
47
48 public GraphPositionPanel() {
49 super();
50 init();
51 }
52
53 private void init() {
54 this.setOpaque(true);
55 this.setLayout(null);
56 majorGraduationColor = Color.GRAY;
57 positionDataColor = majorGraduationColor;
58 backgroundColor = Color.BLACK;
59 validSnappingPositions = new boolean[this.getWidth()];
60 graduationSpacingMultiplier = new double[] {2.0, 2.5, 2.0};
61 setGraduationSpacing();
62 }
63
64 @Override
65 public final void paint(final Graphics g) {
66 if (!this.getSize().equals(this.getParent().getSize())) {
67 this.setSize(this.getParent().getSize());
68 }
69 setGraduationSpacing();
70 final Graphics2D g2d = (Graphics2D) g;
71 g2d.setColor(backgroundColor);
72 g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
73 if (genLog == null) {
74 paintPositionBar(g2d, false);
75 } else {
76 if (genLog.getLogStatus() == GenericLog.LogState.LOG_LOADING) {
77 paintPositionBar(g2d, false);
78 } else if (genLog.getLogStatus() == GenericLog.LogState.LOG_LOADED) {
79 final int zoom = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom();
80 final boolean zoomedOut = OpenLogViewer.getInstance().getEntireGraphingPanel().isZoomedOutBeyondOneToOne();
81 if (!zoomedOut || zoom == 1) {
82 setupMouseCursorLineSnappingPositions();
83 }
84 paintPositionBar(g2d, zoomedOut);
85 paintPositionData(g2d, zoomedOut);
86 }
87 }
88 }
89
90 private void paintPositionBar(final Graphics2D g2d, final boolean zoomedOut) {
91 final double graphPosition = OpenLogViewer.getInstance().getEntireGraphingPanel().getGraphPosition();
92 final int zoom = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom();
93 double offset = 0d;
94 double margin = 0d;
95 if (zoomedOut) {
96 offset = majorGraduationSpacing / zoom;
97 offset = Math.ceil(offset);
98 margin = (1d / zoom) / 2d;
99 } else {
100 offset = majorGraduationSpacing * zoom;
101 offset = Math.round(offset);
102 margin = (1d / zoom) / 2d;
103 }
104
105 g2d.setColor(majorGraduationColor);
106
107
108 double nextPositionMarker = getFirstPositionMarkerPlacement();
109
110
111 double position = graphPosition - majorGraduationSpacing;
112 for (int i = -(int) offset; i < this.getWidth() + (int) offset; i++) {
113 if (position >= nextPositionMarker - margin) {
114 int xCoord = i;
115 if (xCoord >= 0 && xCoord < validSnappingPositions.length) {
116 if (validSnappingPositions[xCoord]) {
117
118 } else if (xCoord + 1 < validSnappingPositions.length && validSnappingPositions[xCoord + 1]) {
119 xCoord++;
120 } else if (xCoord > 0 && validSnappingPositions[xCoord - 1]) {
121 xCoord--;
122 }
123 }
124 g2d.drawLine(xCoord, 0, xCoord, 6);
125 nextPositionMarker += majorGraduationSpacing;
126 }
127 if (zoomedOut) {
128 position += zoom;
129 } else {
130 position += (1d / zoom);
131 }
132 }
133 g2d.drawLine(0, 0, this.getWidth(), 0);
134 }
135
136 private void paintPositionData(final Graphics2D g2d, final boolean zoomedOut) {
137 final double graphPosition = OpenLogViewer.getInstance().getEntireGraphingPanel().getGraphPosition();
138 final int zoom = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom();
139 double offset = 0d;
140 double margin = 0d;
141 if (zoomedOut) {
142 offset = majorGraduationSpacing / zoom;
143 offset = Math.ceil(offset);
144 margin = (1d / zoom) / 2d;
145 } else {
146 offset = majorGraduationSpacing * zoom;
147 offset = Math.round(offset);
148 margin = (1d / zoom) / 2d;
149 }
150 g2d.setColor(positionDataColor);
151 final FontMetrics fm = this.getFontMetrics(this.getFont());
152
153
154 double nextPositionMarker = getFirstPositionMarkerPlacement();
155
156
157 double position = graphPosition - majorGraduationSpacing;
158 for (int i = -(int) offset; i < this.getWidth() + (int) offset; i++) {
159 if (position >= nextPositionMarker - margin) {
160 int xCoord = i;
161 if (xCoord >= 0 && xCoord < validSnappingPositions.length) {
162 if (validSnappingPositions[xCoord]) {
163
164 } else if (xCoord + 1 < validSnappingPositions.length && validSnappingPositions[xCoord + 1]) {
165 xCoord++;
166 } else if (xCoord > 0 && validSnappingPositions[xCoord - 1]) {
167 xCoord--;
168 }
169 }
170 String positionDataString = "";
171 BigDecimal positionData = new BigDecimal(nextPositionMarker);
172 if (majorGraduationSpacing > 0.5) {
173 positionDataString = positionData.toPlainString();
174 } else {
175 positionDataString = roundDecimalsOnlyToTwoSignificantFigures(positionData);
176 }
177 final int stringWidth = fm.stringWidth(positionDataString);
178 g2d.drawString(positionDataString, xCoord - (stringWidth / 2), 18);
179
180 nextPositionMarker += majorGraduationSpacing;
181 }
182 if (zoomedOut) {
183 position += zoom;
184 } else {
185 position += (1.0 / zoom);
186 }
187 }
188 }
189
190 private double getFirstPositionMarkerPlacement() {
191 final double graphPosition = OpenLogViewer.getInstance().getEntireGraphingPanel().getGraphPosition();
192
193 double nextPositionMarker = 0d;
194 if (graphPosition < 0d) {
195 while (nextPositionMarker - graphPosition >= majorGraduationSpacing) {
196 nextPositionMarker -= majorGraduationSpacing;
197 }
198 } else {
199 while (nextPositionMarker - graphPosition < 0.0) {
200 nextPositionMarker += majorGraduationSpacing;
201 }
202 }
203
204 nextPositionMarker -= majorGraduationSpacing;
205 return nextPositionMarker;
206 }
207
208 private void setupMouseCursorLineSnappingPositions() {
209 validSnappingPositions = new boolean[this.getWidth()];
210 final double graphPosition = OpenLogViewer.getInstance().getEntireGraphingPanel().getGraphPosition();
211 final int zoom = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom();
212 MultiGraphLayeredPane multiGraph = OpenLogViewer.getInstance().getEntireGraphingPanel().getMultiGraphLayeredPane();
213 final int availableData = (multiGraph.graphSize() - 1) * zoom;
214 long count = Math.round(graphPosition * zoom);
215
216
217 for (int i = 0; i < this.getWidth(); i++) {
218 if (count < -1 || count > availableData + 1 || count % zoom == 0) {
219 validSnappingPositions[i] = true;
220 }
221 count++;
222 }
223 }
224
225 public final void setLog(final GenericLog log) {
226 genLog = log;
227 repaint();
228 }
229
230 private void setGraduationSpacing() {
231 int zoom = 1;
232 boolean zoomedOut = false;
233 if (OpenLogViewer.getInstance() != null) {
234 zoom = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom();
235 zoomedOut = OpenLogViewer.getInstance().getEntireGraphingPanel().isZoomedOutBeyondOneToOne();
236 }
237
238 majorGraduationSpacing = 100.0;
239 int count = (int) (Math.log((double) zoom) / Math.log(2.0));
240
241 if (zoomedOut){
242 for (int i = 0; i < count; i++) {
243 majorGraduationSpacing *= graduationSpacingMultiplier[i % 3];
244
245 }
246 } else {
247 for (int i = 0; i < count; i++) {
248 majorGraduationSpacing /= graduationSpacingMultiplier[i % 3];
249 }
250 }
251 }
252
253 public final int getBestSnappingPosition(final int xMouseCoord) {
254 int bestPosition = xMouseCoord;
255 if (!validSnappingPositions[xMouseCoord]) {
256 boolean found = false;
257 final int startPosition = xMouseCoord;
258 for (int distance = 1; !found; distance++) {
259 final int next = startPosition + distance;
260 final int prev = startPosition - distance;
261 if (next > validSnappingPositions.length - 1 || prev < 0) {
262 bestPosition = xMouseCoord;
263 found = true;
264 } else if (validSnappingPositions[next]) {
265 bestPosition = next;
266 found = true;
267 } else if (validSnappingPositions[prev]) {
268 bestPosition = prev;
269 found = true;
270 }
271 }
272 }
273 return bestPosition;
274 }
275
276
277
278
279
280
281
282
283
284
285
286 private final String roundDecimalsOnlyToTwoSignificantFigures(final BigDecimal input) {
287 String result = "";
288 int indexOfMostSignificantDigit = 0;
289
290
291 result = input.toPlainString();
292 if (result.substring(0, 1).equalsIgnoreCase("-")) {
293 indexOfMostSignificantDigit++;
294 }
295
296
297
298
299
300 int amountOfIntegerDigits = 0;
301 int amountOfLeadingZerosInDecimalPortion = 0;
302 final int indexOfDecimalPoint = result.indexOf('.');
303 if (indexOfDecimalPoint != -1) {
304 amountOfIntegerDigits = result.substring(indexOfMostSignificantDigit, indexOfDecimalPoint).length();
305 boolean done = false;
306 for (int i = indexOfDecimalPoint + 1; i < result.length() && !done; i++) {
307 if (result.substring(i, i).equalsIgnoreCase("0")) {
308 amountOfLeadingZerosInDecimalPortion++;
309 } else {
310 done = true;
311 }
312 }
313
314 } else {
315 amountOfIntegerDigits = result.substring(indexOfMostSignificantDigit).length();
316
317 }
318 final int amountOfDesiredDecimalDigits = amountOfLeadingZerosInDecimalPortion + 2;
319 final int sigFigs = amountOfIntegerDigits + amountOfDesiredDecimalDigits;
320 BigDecimal roundedInput = input.round(new MathContext(sigFigs));
321 roundedInput = roundedInput.stripTrailingZeros();
322 result = roundedInput.toPlainString();
323
324
325 if (result.indexOf('.') == -1) {
326 result = result + ".0";
327 }
328
329
330 if (result.length() > 16) {
331 if (result.substring(0, 16).equalsIgnoreCase("-0.0000000000000")
332 || result.substring(0, 15).equalsIgnoreCase("0.0000000000000")){
333 result = "0.0";
334 }
335 }
336
337 return result;
338 }
339 }