I want to rotate what is painted in a paintComponent(Graphics g)
method according to a rotation angle and around the centroid of the corresponding panel.
Here's what I have initially:
A control panel (textfield + button) to set the angle (in degrees, from 0 to 359). A main panel containing a little white panel. The little white panel has to be updated (setBounds()
and repaint()
) when the user clicks on the "Set angle" button. In any case the little white panel has to be centered on its centroid.
Here's what I want for 90 and 135 degrees:
Here's my SSCCE (which is not working). Could you tell me what I'm doing wrong?
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class RotatingPaint {
private static double angle = 0;
private static JFrame frame = new JFrame();
private static JTextField angleField = new JTextField("0", 6);
private static JButton angleButton = new JButton("Set angle");
private static JPanel controlPanel = new JPanel();
private static JPanel mainPanel = new JPanel();
private static JPanel littleWhitePanel = new JPanel() {
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Color oldColor = g2.getColor();
g2.setColor(Color.RED);
g2.rotate(angle, getWidth() / 2, getHeight() / 2);
g2.drawString("Hello World", 2, 12);
// Revert the transformation matrix back to its initial state
g2.rotate(-angle, -getWidth() / 2, -getHeight() / 2);
g2.setColor(oldColor);
}
};
public RotatingPaint() {
// Inits control panel
angleButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e){
angle = Math.toRadians(Integer.parseInt(angleField.getText()));
Rectangle oldBounds = littleWhitePanel.getBounds();
Rectangle newBounds = getBoundsOfRotatedRectangle(oldBounds);
littleWhitePanel.setBounds(newBounds);
mainPanel.repaint();
}
});
controlPanel.add(angleField);
controlPanel.add(angleButton);
// Inits main panel
mainPanel.setPreferredSize(new Dimension(400, 300));
mainPanel.setLayout(null);
littleWhitePanel.setBounds(160, 100, 80, 20);
littleWhitePanel.setBackground(Color.WHITE);
mainPanel.add(littleWhitePanel);
// Inits frame
frame.setLayout(new BorderLayout());
frame.add(controlPanel, BorderLayout.NORTH);
frame.add(mainPanel, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
RotatingPaint app = new RotatingPaint();
}
/**
* Applies the rotate transformation to a Point.
* @param point the point to be rotated
*/
private void rotatePoint(Point point) {
Point centroid = new Point((int) littleWhitePanel.getBounds().getCenterX(), (int) littleWhitePanel.getBounds().getCenterY());
int x = (int) Math.rint(centroid.x
+ (point.x - centroid.x) * Math.cos(angle)
- (point.y - centroid.y) * Math.sin(angle));
int y = (int) Math.rint(centroid.y
+ (point.x - centroid.x) * Math.sin(angle)
+ (point.y - centroid.y) * Math.cos(angle));
point.x = x;
point.y = y;
}
/**
* @param rectangle the rectangle to be rotated
* @return the bounding rectangle of the rotated rectangle
*/
private Rectangle getBoundsOfRotatedRectangle(Rectangle rectangle) {
// Getting each corner
List<Point> corners = new ArrayList<Point>();
Point topLeftCorner = rectangle.getLocation();
corners.add(topLeftCorner);
Point topRightCorner = new Point(topLeftCorner);
topRightCorner.x += rectangle.width;
corners.add(topRightCorner);
Point bottomLeftCorner = new Point(topLeftCorner);
bottomLeftCorner.y += rectangle.height;
corners.add(bottomLeftCorner);
Point bottomRightCorner = new Point(bottomLeftCorner);
bottomRightCorner.x += rectangle.width;
corners.add(bottomRightCorner);
// Transforming each corner
for (Point corner : corners) {
rotatePoint(corner);
}
// Getting the min/max x and the min/max y
int minX = corners.get(0).x;
int minY = corners.get(0).y;
int maxX = corners.get(0).x;
int maxY = corners.get(0).y;
for (int i = 1; i < corners.size(); i++) {
minX = Math.min(minX, corners.get(i).x);
minY = Math.min(minY, corners.get(i).y);
maxX = Math.max(maxX, corners.get(i).x);
maxY = Math.max(maxY, corners.get(i).y);
}
return new Rectangle(minX, minY, maxX - minX, maxY - minY);
}
}
I manage to reset the bounds of the little white panel but I'm not able to rotate the text correctly. It's pretty hard to describe what isn't working precisely. Please execute that code and check by yourself.
Thank you.