• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright 2011 See AUTHORS file.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  ******************************************************************************/
16 
17 package com.badlogic.gdx.tools.particleeditor;
18 
19 import java.awt.BasicStroke;
20 import java.awt.Color;
21 import java.awt.Font;
22 import java.awt.FontMetrics;
23 import java.awt.Graphics;
24 import java.awt.Graphics2D;
25 import java.awt.GridBagLayout;
26 import java.awt.event.MouseAdapter;
27 import java.awt.event.MouseEvent;
28 import java.awt.event.MouseMotionListener;
29 import java.util.ArrayList;
30 
31 import javax.swing.JPanel;
32 
33 public class Chart extends JPanel {
34 	static private final int POINT_SIZE = 6;
35 	static private final int POINT_SIZE_EXPANDED = 10;
36 
37 	ArrayList<Point> points = new ArrayList();
38 	private int numberHeight;
39 	int chartX, chartY;
40 	int chartWidth, chartHeight;
41 	int maxX, maxY;
42 	int overIndex = -1;
43 	int movingIndex = -1;
44 	boolean isExpanded;
45 	String title;
46 
47 	boolean moveAll = false;
48 	boolean moveAllProportionally = false;
49 	int moveAllPrevY;
50 
Chart(String title)51 	public Chart (String title) {
52 		this.title = title;
53 
54 		setLayout(new GridBagLayout());
55 
56 		addMouseListener(new MouseAdapter() {
57 			public void mousePressed (MouseEvent event) {
58 				movingIndex = overIndex;
59 				moveAll = event.isControlDown();
60 				if (moveAll) {
61 					moveAllProportionally = event.isShiftDown();
62 					moveAllPrevY = event.getY();
63 				}
64 			}
65 
66 			public void mouseReleased (MouseEvent event) {
67 				movingIndex = -1;
68 				moveAll = false;
69 			}
70 
71 			public void mouseClicked (MouseEvent event) {
72 				if (event.getClickCount() == 2) {
73 					if (overIndex <= 0 || overIndex >= points.size()) return;
74 					points.remove(overIndex);
75 					pointsChanged();
76 					repaint();
77 					return;
78 				}
79 				if (movingIndex != -1) return;
80 				if (overIndex != -1) return;
81 				int mouseX = event.getX();
82 				int mouseY = event.getY();
83 				if (mouseX < chartX || mouseX > chartX + chartWidth) return;
84 				if (mouseY < chartY || mouseY > chartY + chartHeight) return;
85 				Point newPoint = pixelToPoint(mouseX, mouseY);
86 				int i = 0;
87 				Point lastPoint = null;
88 				for (Point point : points) {
89 					if (point.x > newPoint.x) {
90 						if (Math.abs(point.x - newPoint.x) < 0.001f) return;
91 						if (lastPoint != null && Math.abs(lastPoint.x - newPoint.x) < 0.001f) return;
92 						points.add(i, newPoint);
93 						overIndex = i;
94 						pointsChanged();
95 						repaint();
96 						return;
97 					}
98 					lastPoint = point;
99 					i++;
100 				}
101 				overIndex = points.size();
102 				points.add(newPoint);
103 				pointsChanged();
104 				repaint();
105 			}
106 		});
107 		addMouseMotionListener(new MouseMotionListener() {
108 			public void mouseDragged (MouseEvent event) {
109 				if (movingIndex == -1 || movingIndex >= points.size()) return;
110 				if (moveAll){
111 					int newY = event.getY();
112 					float deltaY = (moveAllPrevY - newY) / (float)chartHeight * maxY;
113 					for (Point point : points){
114 						point.y = Math.min(maxY, Math.max(0, point.y + (moveAllProportionally ? deltaY * point.y : deltaY)));
115 					}
116 					moveAllPrevY = newY;
117 				} else {
118 					float nextX = movingIndex == points.size() - 1 ? maxX : points.get(movingIndex + 1).x - 0.001f;
119 					if (movingIndex == 0) nextX = 0;
120 					float prevX = movingIndex == 0 ? 0 : points.get(movingIndex - 1).x + 0.001f;
121 					Point point = points.get(movingIndex);
122 					point.x = Math.min(nextX, Math.max(prevX, (event.getX() - chartX) / (float)chartWidth * maxX));
123 					point.y = Math.min(maxY, Math.max(0, chartHeight - (event.getY() - chartY)) / (float)chartHeight * maxY);
124 				}
125 				pointsChanged();
126 				repaint();
127 			}
128 
129 			public void mouseMoved (MouseEvent event) {
130 				int mouseX = event.getX();
131 				int mouseY = event.getY();
132 				int oldIndex = overIndex;
133 				overIndex = -1;
134 				int pointSize = isExpanded ? POINT_SIZE_EXPANDED : POINT_SIZE;
135 				int i = 0;
136 				for (Point point : points) {
137 					int x = chartX + (int)(chartWidth * (point.x / (float)maxX));
138 					int y = chartY + chartHeight - (int)(chartHeight * (point.y / (float)maxY));
139 					if (Math.abs(x - mouseX) <= pointSize && Math.abs(y - mouseY) <= pointSize) {
140 						overIndex = i;
141 						break;
142 					}
143 					i++;
144 				}
145 				if (overIndex != oldIndex) repaint();
146 			}
147 		});
148 	}
149 
addPoint(float x, float y)150 	public void addPoint (float x, float y) {
151 		points.add(new Point(x, y));
152 	}
153 
pointsChanged()154 	public void pointsChanged () {
155 	}
156 
getValuesX()157 	public float[] getValuesX () {
158 		float[] values = new float[points.size()];
159 		int i = 0;
160 		for (Point point : points)
161 			values[i++] = point.x;
162 		return values;
163 	}
164 
getValuesY()165 	public float[] getValuesY () {
166 		float[] values = new float[points.size()];
167 		int i = 0;
168 		for (Point point : points)
169 			values[i++] = point.y;
170 		return values;
171 	}
172 
setValues(float[] x, float[] y)173 	public void setValues (float[] x, float[] y) {
174 		points.clear();
175 		for (int i = 0; i < x.length; i++)
176 			points.add(new Point(x[i], y[i]));
177 	}
178 
pixelToPoint(float x, float y)179 	Point pixelToPoint (float x, float y) {
180 		Point point = new Point();
181 		point.x = Math.min(maxX, Math.max(0, x - chartX) / (float)chartWidth * maxX);
182 		point.y = Math.min(maxY, Math.max(0, chartHeight - (y - chartY)) / (float)chartHeight * maxY);
183 		return point;
184 	}
185 
pointToPixel(Point point)186 	Point pointToPixel (Point point) {
187 		Point pixel = new Point();
188 		pixel.x = chartX + (int)(chartWidth * (point.x / (float)maxX));
189 		pixel.y = chartY + chartHeight - (int)(chartHeight * (point.y / (float)maxY));
190 		return pixel;
191 	}
192 
paintComponent(Graphics graphics)193 	protected void paintComponent (Graphics graphics) {
194 		// setOpaque(true);
195 		// setBackground(Color.red);
196 		super.paintComponent(graphics);
197 
198 		Graphics2D g = (Graphics2D)graphics;
199 		FontMetrics metrics = g.getFontMetrics();
200 		if (numberHeight == 0) {
201 			numberHeight = getFont().layoutGlyphVector(g.getFontRenderContext(), new char[] {'0'}, 0, 1, Font.LAYOUT_LEFT_TO_RIGHT)
202 				.getGlyphPixelBounds(0, g.getFontRenderContext(), 0, 0).height;
203 		}
204 
205 		int width = getWidth();
206 		if (!isExpanded) width = Math.min(150, width);
207 		width = Math.max(100, width);
208 		int height = getHeight();
209 		int maxAxisLabelWidth;
210 		int yAxisWidth;
211 		if (isExpanded) {
212 			maxAxisLabelWidth = metrics.stringWidth("100%");
213 			yAxisWidth = maxAxisLabelWidth + 8;
214 			chartX = yAxisWidth;
215 			chartY = numberHeight / 2 + 1;
216 			chartWidth = width - yAxisWidth - 2;
217 			chartHeight = height - chartY - numberHeight - 8;
218 		} else {
219 			maxAxisLabelWidth = 0;
220 			yAxisWidth = 2;
221 			chartX = yAxisWidth;
222 			chartY = 2;
223 			chartWidth = width - yAxisWidth - 2;
224 			chartHeight = height - chartY - 3;
225 		}
226 
227 		g.setColor(Color.white);
228 		g.fillRect(chartX, chartY, chartWidth, chartHeight);
229 		g.setColor(Color.black);
230 		g.drawRect(chartX, chartY, chartWidth, chartHeight);
231 
232 		maxX = 1;
233 		{
234 			int y = height;
235 			if (isExpanded)
236 				y -= numberHeight;
237 			else
238 				y += 5;
239 			int xSplit = (int)Math.min(10, chartWidth / (maxAxisLabelWidth * 1.5f));
240 			for (int i = 0; i <= xSplit; i++) {
241 				float percent = i / (float)xSplit;
242 				String label = axisLabel(maxX * percent);
243 				int labelWidth = metrics.stringWidth(label);
244 				int x = (int)(yAxisWidth + chartWidth * percent);
245 				if (i != 0 && i != xSplit) {
246 					g.setColor(Color.lightGray);
247 					g.drawLine(x, chartY + 1, x, chartY + chartHeight);
248 					g.setColor(Color.black);
249 				}
250 				g.drawLine(x, y - 4, x, y - 8);
251 				if (isExpanded) {
252 					x -= labelWidth / 2;
253 					if (i == xSplit) x = Math.min(x, width - labelWidth);
254 					g.drawString(label, x, y + numberHeight);
255 				}
256 			}
257 		}
258 
259 		maxY = 1;
260 		{
261 			int ySplit = isExpanded ? Math.min(10, chartHeight / (numberHeight * 3)) : 4;
262 			for (int i = 0; i <= ySplit; i++) {
263 				float percent = i / (float)ySplit;
264 				String label = axisLabel(maxY * percent);
265 				int labelWidth = metrics.stringWidth(label);
266 				int y = (int)(chartY + chartHeight - chartHeight * percent);
267 				if (isExpanded) g.drawString(label, yAxisWidth - 6 - labelWidth, y + numberHeight / 2);
268 				if (i != 0 && i != ySplit) {
269 					g.setColor(Color.lightGray);
270 					g.drawLine(chartX, y, chartX + chartWidth - 1, y);
271 					g.setColor(Color.black);
272 				}
273 				g.drawLine(yAxisWidth - 4, y, yAxisWidth, y);
274 			}
275 		}
276 
277 		{
278 			int titleWidth = metrics.stringWidth(title);
279 			int x = yAxisWidth + chartWidth / 2 - titleWidth / 2;
280 			int y = chartY + chartHeight / 2 - numberHeight / 2;
281 			g.setColor(Color.white);
282 			g.fillRect(x - 2, y - 2, titleWidth + 4, numberHeight + 4);
283 			g.setColor(Color.lightGray);
284 			g.drawString(title, x, y + numberHeight);
285 		}
286 
287 		g.setColor(Color.blue);
288 		g.setStroke(new BasicStroke(isExpanded ? 3 : 2));
289 		int lastX = -1, lastY = -1;
290 		for (Point point : points) {
291 			Point pixel = pointToPixel(point);
292 			if (lastX != -1) g.drawLine(lastX, lastY, (int)pixel.x, (int)pixel.y);
293 			lastX = (int)pixel.x;
294 			lastY = (int)pixel.y;
295 		}
296 		g.drawLine(lastX, lastY, chartX + chartWidth - 1, lastY);
297 		for (int i = 0, n = points.size(); i < n; i++) {
298 			Point point = points.get(i);
299 			Point pixel = pointToPixel(point);
300 			if (overIndex == i)
301 				g.setColor(Color.red);
302 			else
303 				g.setColor(Color.black);
304 			String label = valueLabel(point.y);
305 			int labelWidth = metrics.stringWidth(label);
306 			int pointSize = isExpanded ? POINT_SIZE_EXPANDED : POINT_SIZE;
307 			int x = (int)pixel.x - pointSize / 2;
308 			int y = (int)pixel.y - pointSize / 2;
309 			g.fillOval(x, y, pointSize, pointSize);
310 			if (isExpanded) {
311 				g.setColor(Color.black);
312 				x = Math.max(chartX + 2, Math.min(chartX + chartWidth - labelWidth, x));
313 				y -= 3;
314 				if (y < chartY + numberHeight + 3)
315 					y += 27;
316 				else if (n > 1) {
317 					Point comparePoint = i == n - 1 ? points.get(i - 1) : points.get(i + 1);
318 					if (y < chartY + chartHeight - 27 && comparePoint.y > point.y) y += 27;
319 				}
320 				g.drawString(label, x, y);
321 			}
322 		}
323 	}
324 
valueLabel(float value)325 	private String valueLabel (float value) {
326 		value = (int)(value * 1000) / 10f;
327 		if (value % 1 == 0)
328 			return String.valueOf((int)value) + '%';
329 		else
330 			return String.valueOf(value) + '%';
331 	}
332 
axisLabel(float value)333 	private String axisLabel (float value) {
334 		value = (int)(value * 100);
335 		if (value % 1 == 0)
336 			return String.valueOf((int)value) + '%';
337 		else
338 			return String.valueOf(value) + '%';
339 	}
340 
341 	static public class Point {
342 		public float x;
343 		public float y;
344 
Point()345 		public Point () {
346 		}
347 
Point(float x, float y)348 		public Point (float x, float y) {
349 			this.x = x;
350 			this.y = y;
351 		}
352 	}
353 
isExpanded()354 	public boolean isExpanded () {
355 		return isExpanded;
356 	}
357 
setExpanded(boolean isExpanded)358 	public void setExpanded (boolean isExpanded) {
359 		this.isExpanded = isExpanded;
360 	}
361 
setTitle(String title)362 	public void setTitle(String title){
363 		this.title = title;
364 	}
365 }
366