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