6
In the question "How to rotate an arrow inside a circle using Java2d?", i managed to learn how to make the arrow rotate within the circle. Only I need to make the arrow rotate like a casino roulette, until I stop alone.
I thought I’d use SwingWorker
or separate into a Thread
. I read some interesting suggestions in this answer on Soen but I’m not getting to apply in my code.
I tried to create a Thread
in the ActionListener
button rotateButton
but I couldn’t time it to rotate the arrow automatically with one click. I thought to use swing.Timer
, but I couldn’t automate it to stop without intervention too.
How do I do that?
Here is a compileable example from the answer to the linked question at the beginning of this post:
import java.awt.*;
import java.awt.geom.AffineTransform;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class SpinArrowTest extends JFrame {
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
| UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
EventQueue.invokeLater(() -> new SpinArrowTest().setVisible(true));
}
public SpinArrowTest() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setPreferredSize(new Dimension(400, 300));
JPanel contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
Board board = new Board();
contentPane.add(board, BorderLayout.CENTER);
JPanel controlsPane = new JPanel(new GridLayout(0, 1, 0, 0));
controlsPane.setBorder(new EmptyBorder(5, 1, 1, 1));
JButton rotateButton = new JButton("Rotate");
rotateButton.addActionListener(e -> board.spin());
controlsPane.add(rotateButton);
contentPane.add(controlsPane, BorderLayout.SOUTH);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
}
//painel principal onde ocorrerá a animação e desenho
class Board extends JPanel {
private static final long serialVersionUID = 1L;
private double angleDegrees; // Em graus.
public Board() {
angleDegrees = 90;
}
public void spin() {
angleDegrees += 10;
angleDegrees %= 360;
repaint();
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.addRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
super.paintComponent(g);
int widthRectangle = getWidth();
int heightReclangle = getHeight();
int x, y, diameter;
if (widthRectangle <= heightReclangle) {
diameter = widthRectangle;
y = heightReclangle / 2 - diameter / 2;
x = 0;
} else {
diameter = heightReclangle;
x = widthRectangle / 2 - diameter / 2;
y = 0;
}
Circle circle = new Circle(x, y, diameter, Color.red);
circle.draw(g);
LineArrow line = new LineArrow(x + diameter / 2, y + diameter / 2, angleDegrees, diameter / 2, Color.white, 3, 15);
line.draw(g);
}
}
//CLASSE QUE REPRESENTA O CIRCULO
class Circle {
private final int x;
private final int y;
private final int diameter;
private final Color color;
public Circle(int x, int y, int diameter, Color color) {
super();
this.x = x;
this.y = y;
this.diameter = diameter;
this.color = color;
}
public void draw(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(color);
g2.setPaint(new GradientPaint(x, y, color, x + diameter / 2, y + diameter / 2, color.darker()));
g2.fillOval(x, y, diameter, diameter);
}
}
//CLASSE QUE REPRESENTA A SETA QUE IRÁ GIRAR DENTRO DO CIRCULO
class LineArrow {
private final int x;
private final int y;
private final int endX;
private final int endY;
private final double angleRadians;
private final Color color;
private final int thickness;
private final double scale;
private static final int TRIANGLE_LENGTH = 2;
private static final Polygon ARROW_HEAD = new Polygon();
static {
ARROW_HEAD.addPoint(TRIANGLE_LENGTH, 0);
ARROW_HEAD.addPoint(0, -TRIANGLE_LENGTH / 2);
ARROW_HEAD.addPoint(0, TRIANGLE_LENGTH / 2);
}
public LineArrow(int x, int y, double angleDegrees, int length, Color color, int thickness, int headSize) {
super();
this.x = x;
this.y = y;
this.color = color;
this.thickness = thickness;
// Converte o ângulo para radianos.
this.angleRadians = Math.toRadians(angleDegrees);
// Calcula a escala a ser aplicada ao desenhar a ponta.
this.scale = headSize / TRIANGLE_LENGTH;
// Calcula a posição final da linha de acordo com o ângulo e com o
// comprimento. Corta do comprimento o tamanho da ponta.
this.endX = (int) (x + (length - headSize) * Math.cos(angleRadians));
this.endY = (int) (y + (length - headSize) * Math.sin(angleRadians));
}
public void draw(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
// Define a cor e a espessura da linha.
g2.setColor(color);
g2.setStroke(new BasicStroke(thickness));
// Desenha a linha.
g2.drawLine(x, y, endX, endY);
// Obtém o AffineTransform original.
AffineTransform tx1 = g2.getTransform();
// Cria uma cópia do AffineTransform.
AffineTransform tx2 = (AffineTransform) tx1.clone();
// Translada e rotaciona o novo AffineTransform.
tx2.translate(endX, endY);
tx2.scale(scale, scale);
tx2.rotate(angleRadians);
// Desenha a ponta com o AffineTransform transladado e rotacionado.
g2.setTransform(tx2);
g2.fill(ARROW_HEAD);
// Restaura o AffineTransform original.
g2.setTransform(tx1);
}
}
I guess I’ll never get used to the operator module on floating point. I know it makes all the sense in the world, but it’s still something that doesn’t go to my head
– Jefferson Quesado
@Jeffersonquesado Imagine you have two floating point numbers
x
andy
. How much isx % y
? Just subtracty
ofx
consecutive times until you get a smaller number thanx
, that’s the rest.– Victor Stafusa
I saw your answer and loved how you came to the conclusion: https://answall.com/a/178432/64969 I am working my mind to accept the module operation out of the integers
– Jefferson Quesado
Is it possible to fix the initial speed? This would result in some strange behavior, for example, the arrow always stopping at the same point?
– user28595
Another question, in Publish you pass only one Double, from where arose that list of process doubles?
– user28595
@If initial velocity and friction are fixed, it would always stop at the same point. One of the two should vary. This part of the
publish
is explained in the first link, and is a feature of theSwingWorker
. With thepublish
, i publish intermediate results that will be consumed onprocess
. When theprocess
run, there may be several accumulated intermediate results to be processed, and so we have a list.– Victor Stafusa
@Victorstafusa I even understand how Publish/process works, but when I used, I usually use (e.g., progress bar) a loop to publish, you didn’t use any loops to publish, I imagine that the process is responsible for changing the position of the arrow, but I don’t understand how it works without loop in Publish.
– user28595
@Articuno O
scheduleAtFixedRate
causes theRunnable
is called recurring and periodic. This is the loop.– Victor Stafusa
True, I forgot that detail. What is happening here:
double distanciaAngular = doubles.stream().reduce(Double::sum).orElse(0.0);
? I don’t know much about streams.– user28595
@Articuno It’s a more abbreviated way of doing
double distanciaAngular = 0.0; for (Double d : doubles) { distanciaAngular += d; }
- Basically, thereduce(Double::sum)
will add up all theDoubles
and produce aOptional<Double>
. Is aOptional
because it would be empty in case thereduce
be applied in aStream
empty. With the methodorElse
i give the value to be used when theOptional
come empty (though in this case it should never happen).– Victor Stafusa
Ah understood, that ai represents the increment of the values passed by Publish, make the arrow move, similar to an increment of the progress bar.
– user28595
@Articuno Exatamente.
– Victor Stafusa