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