Previously, I asked " How to draw an arrow using Java2D? and now with the arrow drawn correctly and positioned within my circle, I would like to have the arrow turn inside the circle so that the center of the circle is the fixed reference point of one of the arrowheads.
I was told that to calculate the rotation, I would need to use the formula:
A = {x + L × cos (θ), and + L × sin (θ)}
where x
and y
are the coordinates at the new point of rotation, L
would be the size of the arrow and θ
would be the angle of rotation.
Although I understand the formula, I'm not sure how to apply in my class LineArrow
, even because I draw it based on coordinates, and how it can change angle, I'm not sure how to calculate its size, regardless of position where it is in the circle. In the example I used the vertical position, but this position could be any one. I also do not know if it is the ideal to apply in this code.
My class LineArrow
is as follows:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.geom.AffineTransform;
public class LineArrow {
private int x;
private int y;
private int endX;
private int endY;
private Color color;
private int thickness;
private static final Polygon ARROW_HEAD = new Polygon();
static {
ARROW_HEAD.addPoint(0, 0);
ARROW_HEAD.addPoint(-5, -10);
ARROW_HEAD.addPoint(5, -10);
}
public LineArrow(int x, int y, int x2, int y2, Color color, int thickness) {
super();
this.x = x;
this.y = y;
this.endX = x2;
this.endY = y2;
this.color = color;
this.thickness = thickness;
}
public void draw(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
// Calcula o ângulo da seta.
double angle = Math.atan2(endY - y, endX - x);
g2.setColor(color);
g2.setStroke(new BasicStroke(thickness));
// Desenha a linha. Corta 10 pixels na ponta para a ponta não ficar
// grossa.
g2.drawLine(x, y, (int) (endX - 10 * Math.cos(angle)), (int) (endY - 10 * Math.sin(angle)));
// 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(thickness / 2, thickness / 2);
tx2.rotate(angle - Math.PI / 2);
// Desenha a ponta com o AffineTransform transladado e rotacionado.
g2.setTransform(tx2);
g2.fill(ARROW_HEAD);
// Restaura o AffineTransform original.
g2.setTransform(tx1);
}
public void spin() {
// ????
}
}
Here's a compilable example:
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;
private JPanel contentPane;
private JPanel board;
private JPanel controlsPane;
private JButton rotateButton;
public static void main(String[] args) {
EventQueue.invokeLater(() -> new SpinArrowTest().setVisible(true));
}
public SpinArrowTest() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setPreferredSize(new Dimension(400, 300));
this.contentPane = new JPanel();
this.contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
this.contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(this.contentPane);
this.board = new Board();
this.contentPane.add(this.board, BorderLayout.CENTER);
this.controlsPane = new JPanel(new GridLayout(0, 1, 0, 0));
this.controlsPane.setBorder(new EmptyBorder(5, 1, 1, 1));
this.rotateButton = new JButton("Rotate");
this.rotateButton.addActionListener(e -> {
});
this.controlsPane.add(this.rotateButton);
this.contentPane.add(this.controlsPane, BorderLayout.SOUTH);
pack();
}
}
Class Main panel where animation and drawing will take place:
class Board extends JPanel {
private static final long serialVersionUID = 1L;
private Circle circle;
private LineArrow line;
public void spin() {
line.spin();
repaint();
}
@Override
protected void paintComponent(Graphics g) {
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 = new Circle(x, y, diameter, Color.red);
circle.draw(g);
line = new LineArrow(x + diameter / 2, y + diameter / 2, x + diameter / 2, y + diameter, Color.white, 3);
line.draw(g);
}
}
Class representing the circle:
class Circle {
int x;
int y;
int diameter;
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);
}
}
Class representing the arrow that will rotate within the circle
class LineArrow {
private int x;
private int y;
private int endX;
private int endY;
private Color color;
private int thickness;
private static final Polygon ARROW_HEAD = new Polygon();
static {
ARROW_HEAD.addPoint(0, 0);
ARROW_HEAD.addPoint(-5, -10);
ARROW_HEAD.addPoint(5, -10);
}
public LineArrow(int x, int y, int x2, int y2, Color color, int thickness) {
super();
this.x = x;
this.y = y;
this.endX = x2;
this.endY = y2;
this.color = color;
this.thickness = thickness;
}
public void draw(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
// Calcula o ângulo da seta.
double angle = Math.atan2(endY - y, endX - x);
g2.setColor(color);
g2.setStroke(new BasicStroke(thickness));
// Desenha a linha. Corta 10 pixels na ponta para a ponta não ficar
// grossa.
g2.drawLine(x, y, (int) (endX - 10 * Math.cos(angle)), (int) (endY - 10 * Math.sin(angle)));
// 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(thickness / 2, thickness / 2);
tx2.rotate(angle - Math.PI / 2);
// Desenha a ponta com o AffineTransform transladado e rotacionado.
g2.setTransform(tx2);
g2.fill(ARROW_HEAD);
// Restaura o AffineTransform original.
g2.setTransform(tx1);
}
public void spin() {
// ????
}
}
The result is the figure below (static):