How to rotate something with Graphics2D - java

So I want to rotate this Rectangle I made
public void paint (Graphics g)
{
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
g2.fillRect(10, 10, 30, 30);
g2.rotate(Math.toRadians(45)); //I tried this but doesn't seem to work...
}
How do I do that? Rotate as in rotate in 45* angle or 200* angle.

It really isn't that hard to rotate objects. Most of the code below is simply boiler plate to create the frames and panels. Here is a demo that shows two methods that were mentioned in the comments.
the left panel simply rotates the graphics context. This is, imo, the easiest method but it does not alter the object.
the right panel uses the AffineTransform to rotate the object. This actually changes the contents of the shape.
If the desire is to rotate an object in place it is necessary to ensure one is rotating about the middle of the image that is under rotation. In both cases below that would be (125,125) or the center of both the panels and the rectangle.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.border.LineBorder;
public class RotateRectangle extends JPanel {
JFrame frame = new JFrame();
double angle = 0;
MyPanel mypanel = new MyPanel();
public static void main(String[] args) {
SwingUtilities
.invokeLater(() -> new RotateRectangle().start());
}
public void start() {
Timer t = new Timer(0, (ae) -> {mypanel.rotateit(); frame.repaint();});
t.setDelay(30);
t.start();
}
public RotateRectangle() {
frame.setLayout(new FlowLayout());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this);
frame.add(mypanel);
setBorder(new LineBorder(Color.red,2));
mypanel.setBorder(new LineBorder(Color.red, 2));
frame.pack();
// center on screen
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
// visually smooth the lines
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint(Color.BLACK);
g2d.rotate(angle, 125,125);
g2d.drawRect(75, 75, 100, 100);
// adjust the amount of rotation per timer interval
angle += Math.PI / 200;
g2d.dispose();
}
public Dimension getPreferredSize() {
return new Dimension(250, 250);
}
}
class MyPanel extends JPanel {
Polygon polygon = new Polygon();
// amount to rotate
double angle = Math.PI / 200;
Shape shape = polygon;
AffineTransform af = new AffineTransform();
public MyPanel() {
af.rotate(angle, 125,125);
polygon.addPoint(75,175);
polygon.addPoint(175,175);
polygon.addPoint(175,75);
polygon.addPoint(75,75);
}
public void rotateit() {
shape = af.createTransformedShape(shape);
}
public void paintComponent(Graphics g) {
if (shape == null) {
return;
}
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
// visually smooth the lines
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint(Color.BLACK);
g2d.draw(shape);
}
public Dimension getPreferredSize() {
return new Dimension(250, 250);
}
}

Following up on my comment, I created the following GUI.
I used math to calculate the four endpoints of a rotated rectangle and used the Graphics2D fillPolygon method to draw the rectangle.
The buttons at the bottom of the GUI allow you to rotate the rectangle on the center point or the upper left endpoint.
I created a drawing JPanel to draw the rectangle. All the paintComponent method of the drawing JPanel does is draw the Polygon returned by the application model.
The application model is a key part of this application. I create a plain Java getter / setter class. I start with a java.awt.Rectangle and use polar coordinates to rotate the rectangle. I convert the polar coordinates back to cartesian coordinates to get the four endpoints of the Polygon.
Here's the complete runnable code.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class RotatingRectangle implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new RotatingRectangle());
}
private DrawingPanel drawingPanel;
private JButton centerButton;
private JButton endPointButton;
private RotatedRectangle rotatedRectangle;
public RotatingRectangle() {
this.rotatedRectangle = new RotatedRectangle(Color.BLUE,
new Rectangle(200, 200, 100, 50));
}
#Override
public void run() {
JFrame frame = new JFrame("Rotating Rectangle");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.drawingPanel = new DrawingPanel(rotatedRectangle);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.add(createButtonPanel(), BorderLayout.AFTER_LAST_LINE);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public JPanel createButtonPanel() {
JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 5));
ButtonListener listener = new ButtonListener(this, rotatedRectangle);
centerButton = new JButton("Rotate on center point");
centerButton.addActionListener(listener);
panel.add(centerButton);
endPointButton = new JButton("Rotate on end point");
endPointButton.addActionListener(listener);
endPointButton.setPreferredSize(centerButton.getPreferredSize());
panel.add(endPointButton);
return panel;
}
public void repaint() {
drawingPanel.repaint();
}
public JButton getCenterButton() {
return centerButton;
}
public JButton getEndPointButton() {
return endPointButton;
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private RotatedRectangle rotatedRectangle;
public DrawingPanel(RotatedRectangle rotatedRectangle) {
this.rotatedRectangle = rotatedRectangle;
this.setBackground(Color.WHITE);
this.setPreferredSize(new Dimension(400, 400));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Polygon polygon = rotatedRectangle.getRectangle();
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(rotatedRectangle.getColor());
g2d.fillPolygon(polygon);
}
}
public class ButtonListener implements ActionListener {
private final RotatingRectangle frame;
private final RotatedRectangle model;
private Timer timer;
public ButtonListener(RotatingRectangle frame, RotatedRectangle model) {
this.frame = frame;
this.model = model;
}
#Override
public void actionPerformed(ActionEvent event) {
if (timer != null) {
timer.stop();
}
JButton button = (JButton) event.getSource();
if (button.equals(frame.getEndPointButton())) {
model.setCenterPoint(false);
model.setAngle(180);
} else {
model.setCenterPoint(true);
model.setAngle(0);
}
timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
model.incrementAngle(1);
frame.repaint();
}
});
timer.start();
}
}
public class RotatedRectangle {
private boolean centerPoint;
private int angle;
private final Color color;
private final Rectangle rectangle;
public RotatedRectangle(Color color, Rectangle rectangle) {
this.color = color;
this.rectangle = rectangle;
this.angle = 0;
this.centerPoint = true;
}
public int getAngle() {
return angle;
}
public void setAngle(int angle) {
this.angle = angle;
}
public void incrementAngle(int increment) {
this.angle += increment;
this.angle %= 360;
}
public Polygon getRectangle() {
Point rotatePoint = new Point(rectangle.x, rectangle.y);
if (isCenterPoint()) {
int x = rectangle.x + rectangle.width / 2;
int y = rectangle.y + rectangle.height / 2;
rotatePoint = new Point(x, y);
}
Point[] point = new Point[4];
int width = rectangle.x + rectangle.width;
int height = rectangle.y + rectangle.height;
point[0] = new Point(rectangle.x, rectangle.y);
point[1] = new Point(width, rectangle.y);
point[2] = new Point(width, height);
point[3] = new Point(rectangle.x, height);
Polygon polygon = new Polygon();
for (int i = 0; i < point.length; i++) {
point[i] = calculatePoint(rotatePoint, point[i], angle);
polygon.addPoint(point[i].x, point[i].y);
}
return polygon;
}
private Point calculatePoint(Point rotatePoint, Point point, int angle) {
double theta = Math.toRadians(angle);
int xDistance = rotatePoint.x - point.x;
int yDistance = rotatePoint.y - point.y;
double distance = Math.sqrt(xDistance * xDistance + yDistance * yDistance);
double alpha = Math.atan2(yDistance, xDistance);
theta += alpha;
int x = (int) Math.round(Math.cos(theta) * distance) + rotatePoint.x;
int y = (int) Math.round(Math.sin(theta) * distance) + rotatePoint.y;
return new Point(x, y);
}
public Color getColor() {
return color;
}
public boolean isCenterPoint() {
return centerPoint;
}
public void setCenterPoint(boolean centerPoint) {
this.centerPoint = centerPoint;
}
}
}

Related

Screen Shake like bug on 2D Grid JPanel

I am trying to create a 2d grid on a JPanel with Zoom functionality. The user will draw on the grid then if desired zoom in and out. Image of Grid
When I currently zoom in there is a screen shake like issue/bug when I move the mouse around, which I would like to remove.
The mouse wheel is used to zoom in and out.
Grid to be drawn at certain scale factor:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.AffineTransform;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class DrawExample extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
private double zoom = 1d;
private int tranx;
private int trany;
DrawPanel drawPanel;
int snapmousepositionx, snapmousepositiony;//current snap coords
int currentmousepositionx,currentmousepositiony;
public DrawExample() {
drawPanel = new DrawPanel();
JPanel containerPanel = new JPanel();
JFrame frame = new JFrame(); // Instance of a JFrame
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setSize(800, 800);
//drawPanel.setSize(800, 800);
containerPanel.setLayout(new GridBagLayout());
containerPanel.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(1, 0, 1)));
containerPanel.add(drawPanel);
frame.add(new JScrollPane(containerPanel));
drawPanel.addMouseWheelListener(new MouseAdapter() { //add wheel listener to drawPanel
#Override
public void mouseWheelMoved(MouseWheelEvent e) { //when wheel is moved
if (e.getPreciseWheelRotation() < 0) {
zoom += 0.1;
} else {
zoom -= 0.1;
}
if (zoom < 0.01) {
zoom = 0.01;
}
drawPanel.repaint();
}
});
drawPanel.addMouseMotionListener(new MouseAdapter() {
public void mouseMoved(MouseEvent me) {
super.mouseMoved(me);
drawPanel.createSnapGrid(me.getPoint().x, me.getPoint().y);
tranx=me.getPoint().x;
trany=me.getPoint().y;
drawPanel.repaint();
}
});
frame.setVisible(true);
}
public class DrawPanel extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 800);
}
#Override
protected void paintComponent(Graphics grphcs) {
super.paintComponent(grphcs);
Graphics2D g2d = (Graphics2D) grphcs;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
zoom=Math.round(zoom*10.0)/10.0;
AffineTransform at = g2d.getTransform();
at.translate(tranx, trany);
at.scale(zoom, zoom);
at.translate(-tranx, -trany);
g2d.setTransform(at);
g2d.setColor(Color.lightGray);
drawGrid(g2d);
}
public void createSnapGrid(int x, int y) {
currentmousepositionx = x;
currentmousepositiony = y;
int remainderx = currentmousepositionx % 10, remaindery = currentmousepositiony % 10;
if (remainderx<800/2) setSnapX(currentmousepositionx - remainderx) ;
else setSnapX(currentmousepositionx + (10-remainderx));
if (remaindery<800/2) setSnapY(currentmousepositiony - remaindery);
else setSnapY(currentmousepositiony + (10)-remaindery);
}
}
public void drawGrid(Graphics2D g) {
g.setColor(Color.lightGray);
g.clearRect(0, 0, 800, 800);
System.out.println(getHeight());
//grid vertical lines
for (int i= (10);i<800;i+=10) {
g.drawLine(i, 0, i, 800);
}
//grid horizontal lines
for (int j= (10);j<800;j+=10) {
g.drawLine(0, j, 800, j);
}
//show the snapped point
g.setColor(Color.BLACK);
if ( getSnapX()>=0 && getSnapY()>=0 && getSnapX()<=800 && getSnapY()<=800) {
// result =true;
g.drawOval((int) ( getSnapX())-4, (int) (getSnapY()-4), 8, 8);
}
}
public int getSnapX(){
return (this.snapmousepositionx);
}
public int getSnapY(){
return (this.snapmousepositiony);
}
public void setSnapX(int snap){
this.snapmousepositionx=(int) (snap);
}
public void setSnapY(int snap){
this.snapmousepositiony=(int) (snap);
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new DrawExample();
}
});
}
}

Is there a way to draw boundaries that aren't rectangles in java swing?

I'm trying to draw boundaries for a character that I'm moving around on a JFrame. I know how to draw rectangles to establish boundaries, but how could I do this if the area that I want to draw bounds for isn't a rectangle?
Say for example I want to move the green circle around in the square, but the blue diamond represents the boundaries, and the circle cannot go past those bounds. How could I do this?
This is the kind of thing I use for this is line/point collision:
http://www.jeffreythompson.org/collision-detection/line-circle.php
it's pretty mathy but it works nicely and isn't buggy
Just treat your circle as a rectangle and validate if the circle is inside your diamond with the contains method of Rectangle (See docs). Small snipped:
private Rectangle rect = new Rectangle(25, 25, 450, 450);
private Rectangle circle = new Rectangle(250, 250, 50, 50);
if(rect.contains(tmp)) {
// True, if the circle does not touch any square-line
}
A full example of the code:
package circlemoving;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.*;
import javax.swing.*;
public class CircleMoving {
public static void main(String[] args) {
MainPanel mp = new MainPanel();
JFrame jFrame = new JFrame();
jFrame.add(mp);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setVisible(true);
jFrame.pack();
jFrame.addKeyListener(new KeyListener() {
#Override public void keyTyped(KeyEvent e) {}
#Override public void keyReleased(KeyEvent e) {}
#Override
public void keyPressed(KeyEvent e) {
mp.update(e.getKeyCode());
}
});
}
private static class MainPanel extends JPanel {
private Rectangle rect = new Rectangle(25, 25, 450, 450);
private Rectangle circle = new Rectangle(250, 250, 50, 50);
public MainPanel() {
setPreferredSize(new Dimension(500, 500));
repaint();
}
#Override
public void paintComponent(Graphics grahpics) {
super.paintComponent(grahpics);
Graphics2D g2d = (Graphics2D) grahpics;
g2d.setStroke(new BasicStroke(1));
g2d.setColor(Color.BLUE);
g2d.drawRect(rect.x, rect.y, rect.width, rect.height);
g2d.setStroke(new BasicStroke(1));
g2d.setColor(Color.RED);
g2d.drawOval(circle.x, circle.y, circle.width, circle.width);
}
public void update(int keyCode) {
int tmpX = circle.x;
int tmpY = circle.y;
switch (keyCode) {
case 39:
tmpX = circle.x + 3;
break;
case 37:
tmpX = circle.x - 3;
break;
case 38:
tmpY = circle.y - 3;
break;
case 40:
tmpY = circle.y + 3;
break;
default:
break;
}
Rectangle tmp = new Rectangle(tmpX, tmpY, circle.width, circle.height);
if(rect.contains(tmp)) {
circle.x = tmpX;
circle.y = tmpY;
repaint();
}
}
}
}
Result:
I created a simple GUI to demonstrate how to use a polygon boundary in a Swing GUI. Here's the GUI.
You can't see in the picture, but you can grab the green circle (the player) with the mouse and drag it anywhere inside the blue diamond (the boundary). The code will not let you drag the player outside the boundary.
Here's what I did.
I created two model classes. One for the boundary, and one for the player. I used this logical model to draw in the view and do calculations in the mouse listener. This is an application of the model / view / controller pattern.
I created a simple Swing GUI with a drawing panel. I calculated where the start of the oval would be so I could keep the center point and radius in the model.
I created a mouse listener class to listen for the mouse press and mouse release.
And here's the code. Hopefully, there's not much to explain. The Polygon class has a method, contains, that checks if a rectangle is completely inside the polygon. I converted the circle to a rectangle in the listener so I could use this method.
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Rectangle2D;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
public class PolygonBoundary implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new PolygonBoundary());
}
private Boundary boundary;
private Dimension dpSize;
private Player player;
public PolygonBoundary() {
this.dpSize = new Dimension(400, 400);
Polygon polygon = new Polygon();
polygon.addPoint(200, 0);
polygon.addPoint(0, 200);
polygon.addPoint(200, 400);
polygon.addPoint(400, 200);
this.boundary = new Boundary(polygon, Color.BLUE);
this.player = new Player(200, 200, 24, Color.GREEN);
}
#Override
public void run() {
JFrame frame = new JFrame("Polygon Boundary");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DrawingPanel drawingPanel =
new DrawingPanel(dpSize, boundary, player);
frame.add(drawingPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private Boundary boundary;
private Player player;
public DrawingPanel(Dimension size, Boundary boundary,
Player player) {
this.boundary = boundary;
this.player = player;
this.setPreferredSize(size);
Border border = BorderFactory.createLineBorder(
Color.BLACK, 4);
this.setBorder(border);
PlayerMotion playerMotion =
new PlayerMotion(this, boundary, player);
this.addMouseListener(playerMotion);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(boundary.getColor());
g2d.setStroke(new BasicStroke(4f));
g2d.drawPolygon(boundary.getPolygon());
g2d.setColor(player.getColor());
Point center = player.getCenter();
int radius = player.getRadius();
int diameter = radius + radius;
g2d.drawOval(center.x - radius, center.y - radius,
diameter, diameter);
}
}
public class PlayerMotion implements MouseListener {
private Boundary boundary;
private JPanel panel;
private Player player;
private Point originalPoint;
public PlayerMotion(JPanel panel, Boundary boundary,
Player player) {
this.panel = panel;
this.boundary = boundary;
this.player = player;
}
#Override
public void mouseClicked(MouseEvent event) {
}
#Override
public void mousePressed(MouseEvent event) {
Point center = player.getCenter();
int radius = player.getRadius();
Rectangle2D r2d = createRectangle(center, radius);
if (r2d.contains(event.getPoint())) {
originalPoint = event.getPoint();
}
}
#Override
public void mouseReleased(MouseEvent event) {
if (originalPoint == null) {
return;
}
Point endPoint = event.getPoint();
Point center = player.getCenter();
int radius = player.getRadius();
int newX = center.x - originalPoint.x + endPoint.x;
int newY = center.y - originalPoint.y + endPoint.y;
Point newPoint = new Point(newX, newY);
Rectangle2D r2d = createRectangle(newPoint, radius);
Polygon polygon = boundary.getPolygon();
if (polygon.contains(r2d)) {
player.setCenter(newPoint);
panel.repaint();
}
originalPoint = null;
}
private Rectangle2D createRectangle(Point center, int radius) {
double dx = center.x - radius;
double dy = center.y - radius;
double d = radius + radius;
Rectangle2D r2d = new Rectangle2D.Double(dx, dy, d, d);
return r2d;
}
#Override
public void mouseEntered(MouseEvent event) {
}
#Override
public void mouseExited(MouseEvent event) {
}
}
public class Boundary {
private final Color color;
private final Polygon polygon;
public Boundary(Polygon polygon, Color color) {
this.polygon = polygon;
this.color = color;
}
public Polygon getPolygon() {
return polygon;
}
public Color getColor() {
return color;
}
}
public class Player {
private final int radius;
private final Color color;
private Point center;
public Player(int x, int y, int radius, Color color) {
this.center = new Point(x, y);
this.radius = radius;
this.color = color;
}
public int getRadius() {
return radius;
}
public Point getCenter() {
return center;
}
public void setCenter(Point center) {
this.center = center;
}
public Color getColor() {
return color;
}
}
}

java gui paintComponent refresh

I am learning java gui interface and wrote a program that has a button. Each time the button is clicked, a random sized rectangle will be added to the screen. But instead of adding it to the screen, the program keeps erasing the old one, which I want to keep on the screen. Here is my code. I tried to do paint() and it did not work. Thanks in advance.
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class SimpleGui implements ActionListener {
JFrame frame = new JFrame();
public static void main(String[] args){
SimpleGui gui = new SimpleGui();
gui.go();
}
public void go(){
JButton button = new JButton("Add a rectangle");
MyDrawPanel panel = new MyDrawPanel();
button.addActionListener(this);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(BorderLayout.SOUTH, button);
frame.getContentPane().add(BorderLayout.CENTER, panel);
frame.setSize(300, 300);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event){
frame.repaint();
}
class MyDrawPanel extends JPanel{
public void paintComponent(Graphics g){
g.setColor(Color.blue);
int height = (int) (Math.random()*120 + 10);
int width = (int) (Math.random()*120 + 10);
int x = (int) (Math.random()*40 + 10);
int y = (int) (Math.random()*40 + 10);
g.fillRect(x, y, height, width);
}
}
}
Your paintComponent method is written to draw only one rectangle, so its behavior should come as no shock to you. If you want it to draw multiple, you have one of two options:
Create an ArrayList<Rectangle>, and in the actionPerformed method, add a new random Rectangle to this List and then call repaint(). In the paintComponent method, iterate through this List with a for-loop, painting each Rectangle.
Or you could draw the new random rectangle onto a BufferedImage that is displayed by the paintComponent method.
The first method is the easier of the two, the 2nd is better if you're worried about program responsiveness, say in an animation program.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class TwoDrawRectMethods extends JPanel {
// Array to hold our two drawing JPanels
private AddRandomRect[] addRandomRects = {
new DrawList("Using List"),
new DrawBufferedImage("Using BufferedImage")};
// constructor
public TwoDrawRectMethods() {
// add drawing rectangles onto GUI
for (AddRandomRect addRandomRect : addRandomRects) {
add(addRandomRect);
}
// button to tell rectangles to add a new Rectangle
add(new JButton(new DrawAction("Add New Rectangle")));
}
// The button's Action -- an ActionListener on "steroids"
private class DrawAction extends AbstractAction {
public DrawAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
// tell both drawing JPanels to add a new rectangle
for (AddRandomRect addRandomRect : addRandomRects) {
addRandomRect.addRectangle();
}
}
}
private static void createAndShowGui() {
TwoDrawRectMethods mainPanel = new TwoDrawRectMethods();
JFrame frame = new JFrame("TwoDrawRectMethods");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
#SuppressWarnings("serial")
class DrawList extends AddRandomRect {
private static final Color RECT_COLOR = Color.RED;
private List<Rectangle> rectList = new ArrayList<>();
public DrawList(String title) {
super(title);
}
#Override
public void addRectangle() {
rectList.add(createRandomRect());
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(RECT_COLOR);
for (Rectangle rectangle : rectList) {
g2.draw(rectangle);
}
}
}
#SuppressWarnings("serial")
class DrawBufferedImage extends AddRandomRect {
private static final Color RECT_COLOR = Color.BLUE;
private BufferedImage img = null;
public DrawBufferedImage(String title) {
super(title);
}
#Override
public void addRectangle() {
if (img == null) {
img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
}
Rectangle rect = createRandomRect();
Graphics2D g2 = img.createGraphics();
g2.setColor(RECT_COLOR);
g2.draw(rect);
g2.dispose();
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, null);
}
}
}
#SuppressWarnings("serial")
abstract class AddRandomRect extends JPanel {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private Random random = new Random();
public AddRandomRect(String title) {
setBorder(BorderFactory.createTitledBorder(title));
}
abstract void addRectangle();
protected Rectangle createRandomRect() {
int x1 = random.nextInt(PREF_W);
int x2 = random.nextInt(PREF_W);
int y1 = random.nextInt(PREF_H);
int y2 = random.nextInt(PREF_H);
int x = Math.min(x1, x2);
int y = Math.min(y1, y2);
int width = Math.abs(x1 - x2);
int height = Math.abs(y1 - y2);
return new Rectangle(x, y, width, height);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
}

Sensing hover over outer edge of a Path2D circle in JPanel [duplicate]

This question already has an answer here:
Collision detection with complex shapes
(1 answer)
Closed 8 years ago.
So, I have a program that draws Path2D circles onto JPanel. What I am trying to do is resize the circle when the user clicks and drags the bottom right of the circle. So, what I want is to detect when they are on the bottom-right outer edge of the circle, not the bottom-right of the bounds around the circle. Basically, I need to figure out how to do something like this:
I know how to do this with rectangles using getBounds() but when you use getBounds() on a circle, it will return the square around the circle and not the bounds of the actual circle. Any ideas on how I can get this to work? Thanks!
Here is a shortened, runnable version of my program:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Panel;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Editor {
public static void main(String[] args) {
JFrame frame = new UMLWindow();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(30, 30, 1000, 700);
frame.getContentPane().setBackground(Color.white);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class UMLWindow extends JFrame {
Shapes shapeList = new Shapes();
Panel panel;
private static final long serialVersionUID = 1L;
public UMLWindow() {
addMenus();
panel = new Panel();
}
public void addMenus() {
getContentPane().add(shapeList);
setSize(300, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
shapeList.addCircle(100, 100);
}
}
// Shapes class, used to draw the shapes on the panel
// as well as implements the MouseListener for dragging
class Shapes extends JPanel {
private static final long serialVersionUID = 1L;
private List<Path2D> shapes = new ArrayList<Path2D>();
int currentIndex;
public Shapes() {
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
public void addCircle(int width, int height) {
Path2D circ = new Path2D.Double();
circ.append(new Ellipse2D.Double(442, 269, width, height), true);
shapes.add(circ);
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(2));
for (Path2D shape : shapes) {
g2.draw(shape);
}
}
class MyMouseAdapter extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseDragged(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
}
}
You'll want to brush up your trig (or google search like I did ;)). The basic concept is "relatively" easy, but I created a nice method to all the work for me...
This method...
public Point2D getPointOnEdge(float angel, Rectangle bounds) {
float radius = Math.max(bounds.width, bounds.height) / 2;
float x = radius;
float y = radius;
double rads = Math.toRadians((angel + 90));
// Calculate the outter point of the line
float xPosy = (float) (x + Math.cos(rads) * radius);
float yPosy = (float) (y + Math.sin(rads) * radius);
return new Point2D.Float(xPosy + bounds.x, yPosy + bounds.y);
}
Will calculate the x/y point that a given angle will appear on a circle, remember, this will ONLY work for circles!
I then use another method...
public Rectangle2D getActiveBounds(float angel, Rectangle bounds) {
Point2D p = getPointOnEdge(angel, bounds);
return new Rectangle2D.Double(p.getX() - 4, p.getY() - 4, 8, 8);
}
To calculate the "mouse zone" in which I would consider to be the bottom/right area, cause a single pixel is hard to find and simply use Rectangle#contains, passing it the current mouse location...
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Panel;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Editor {
public static void main(String[] args) {
new Editor();
}
public Editor() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new UMLWindow();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(30, 30, 1000, 700);
frame.getContentPane().setBackground(Color.white);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class UMLWindow extends JFrame {
Shapes shapeList = new Shapes();
Panel panel;
private static final long serialVersionUID = 1L;
public UMLWindow() {
addMenus();
panel = new Panel();
}
public void addMenus() {
getContentPane().add(shapeList);
setSize(300, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
shapeList.addCircle(100, 100);
}
}
// Shapes class, used to draw the shapes on the panel
// as well as implements the MouseListener for dragging
public static class Shapes extends JPanel {
private static final long serialVersionUID = 1L;
private List<Path2D> shapes = new ArrayList<Path2D>();
int currentIndex;
private Point mousePoint;
public Shapes() {
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
public void addCircle(int width, int height) {
Path2D circ = new Path2D.Double();
circ.append(new Ellipse2D.Double(442, 269, width, height), true);
shapes.add(circ);
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(2));
for (Path2D shape : shapes) {
g2.setColor(Color.BLACK);
g2.draw(shape);
g2.setColor(Color.RED);
Rectangle2D bottomRight = getActiveBounds(-45, shape.getBounds());
g2.draw(bottomRight);
if (mousePoint != null) {
if (bottomRight.contains(mousePoint)) {
g2.fill(bottomRight);
}
}
}
}
public Rectangle2D getActiveBounds(float angel, Rectangle bounds) {
Point2D p = getPointOnEdge(angel, bounds);
return new Rectangle2D.Double(p.getX() - 4, p.getY() - 4, 8, 8);
}
public Point2D getPointOnEdge(float angel, Rectangle bounds) {
float radius = Math.max(bounds.width, bounds.height) / 2;
float x = radius;
float y = radius;
double rads = Math.toRadians((angel + 90));
// Calculate the outter point of the line
float xPosy = (float) (x + Math.cos(rads) * radius);
float yPosy = (float) (y + Math.sin(rads) * radius);
return new Point2D.Float(xPosy + bounds.x, yPosy + bounds.y);
}
class MyMouseAdapter extends MouseAdapter {
#Override
public void mouseMoved(MouseEvent e) {
mousePoint = e.getPoint();
repaint();
}
}
}
}
This example does all the work within the paint method, because I wanted to see the "area of effect", you can easily use the same logic to change the mouse cursor within the MouseMoitionListener
Not sure if this will work but you can try something like:
Create a Shape circle that is a couple of pixels smaller than your original Shape
Create a Shape circle that is a couple of pixels larger than your original Shape
Create an Area object using the larger Shape
Create an Area object using the smaller Shape and subtract this Area from the larger Area
Use the contains(...) method to determine if the mouse point is within this Area.

Using Slider to rotate Object In java

I have made a GUI that uses a slider to scale an object up and down.(in this case a rectangle). I was wondering if there was a way to also use a slider to specify a degree of rotation. So there would be 2 sliders one to control scale and another to control the rotation. If anyone could help me make this that would be great here is what I have so far with just the scale slider.
import javax.swing.*;
public class Parker
{
public static void main(String[] args)
{
TheWindow w = new TheWindow();
w.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //X wont close the window with out this line
w.setSize(1280,720);
w.setVisible(true);
}
}
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
public class TheWindow extends JFrame
{
private JSlider slider; //declare slider
private drawRect myPanel; //declare/ create panel
public TheWindow()
{
super("Slider Example"); //make title
myPanel = new drawRect();
myPanel.setBackground(Color.cyan); //change background color
slider = new JSlider(SwingConstants.VERTICAL, 0, 315, 10);// restrains the slider from scaling square to 0-300 pixels
slider.setMajorTickSpacing(20); //will set tick marks every 10 pixels
slider.setPaintTicks(true); //this actually paints the ticks on the screen
slider.addChangeListener
(
new ChangeListener()
{
public void stateChanged(ChangeEvent e)
{
myPanel.setD(slider.getValue()); //Wherever you set the slider, it will pass that value and that will paint on the screen
}
}
);
add(slider, BorderLayout.WEST); //similar to init method, adds slider and panel to GUI
add(myPanel, BorderLayout.CENTER);
}
import java.awt.*;
import javax.swing.*;
public class drawRect extends JPanel
{
private int d = 20; //this determines the beginning size of the rect.
public void paintComponent(Graphics g)//paints obj on the screen
{
super.paintComponent(g); //prepares graphic object for drawing
// ImageIcon i = new ImageIcon("A:\\Capture.png"); //location of Image
// i.paintIcon(this, g, d, d); //paints icon on screen
int originX = getWidth() / 2; //this is subtracting half of 'd' from the center point to scale it form the center
int originY = getHeight() / 2;
int x = originX - (d / 2);
int y = originY - (d / 2);
System.out.println(x + "x" + y);
g.fillRect(x, y, d, d); //paints rectangle on screen
//x , y, width, height
}
Okay, I've been playing around with this for a while I would normally use a AffineTransform for this, but it was giving me weird results I couldn't resolve...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Parker {
public static void main(String[] args) {
new Parker();
}
public Parker() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new ControlPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ControlPane extends JPanel {
private JSlider slider; //declare slider
private DrawPane myPanel;
public ControlPane() {
setLayout(new BorderLayout());
myPanel = new DrawPane();
myPanel.setBackground(Color.cyan); //change background color
slider = new JSlider(SwingConstants.VERTICAL, 0, 400, 100);// restrains the slider from scaling square to 0-300 pixels
slider.setMajorTickSpacing(20); //will set tick marks every 10 pixels
slider.setPaintTicks(true); //this actually paints the ticks on the screen
slider.addChangeListener(
new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
myPanel.setScale(slider.getValue()); //Wherever you set the slider, it will pass that value and that will paint on the screen
}
}
);
JSlider rotate = new JSlider(SwingConstants.VERTICAL, 0, 720, 0);
rotate.setMajorTickSpacing(20); //will set tick marks every 10 pixels
rotate.setPaintTicks(true); //this actually paints the ticks on the screen
rotate.addChangeListener(
new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
JSlider slider = (JSlider) e.getSource();
myPanel.setAngle(slider.getValue());
}
}
);
add(slider, BorderLayout.WEST);
add(rotate, BorderLayout.EAST);
add(myPanel);
myPanel.setScale(400);
}
}
public class DrawPane extends JPanel {
private double scale = 1;
private double angle = 0;
private final int rectWidth = 20;
private final int rectHeight = 20;
#Override
protected void paintComponent(Graphics g)//paints obj on the screen
{
super.paintComponent(g); //prepares graphic object for drawing
int originX = getWidth() / 2;
int originY = getHeight() / 2;
int xOffset = -(rectWidth / 2);
int yOffset = -(rectHeight / 2);
g.setColor(Color.BLACK);
Graphics2D g2d = (Graphics2D) g.create();
g2d.translate(originX, originY);
g2d.scale(scale, scale);
g2d.rotate(Math.toRadians(angle), 0, 0);
g2d.fill(new Rectangle2D.Double(xOffset, yOffset, rectWidth, rectHeight));
g2d.dispose();
g.setColor(Color.RED);
g.drawRect(originX + xOffset, originY + yOffset, rectWidth, rectWidth);
}
public void setAngle(double angle) {
this.angle = angle;
repaint();
}
public void setScale(int scale) {
// Scaling is normalized so that 1 = 100%
this.scale = (scale / 100d);
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
Basically this uses the Graphics APIs capabilities, for simplicity (in particular with the spinning), the Graphics context is translated to the origin point. The rectangle is then paint around this origin point to allow it to be zoomed and rotate about it's center

Categories