The following code works correctly, but I'm having trouble understanding some of the details. Can somebody help me understand how the AffineTransform is working to rotate the image?
package pks;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class rotate {
public static void main(String[] args) {
new rotate();
}
public rotate() {
EventQueue.invokeLater(new Runnable() {
public void run() {
final RotationPane rotationPane = new RotationPane(); // initilize object of RotationPane class
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(rotationPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class RotationPane extends JPanel {
private BufferedImage img;
private BufferedImage rotated;
private double angle;
public RotationPane() {
try {
img = ImageIO.read(new File("C:\\Users\\pardeep\\Desktop\\tomb1.jpg")); // path of the image to be rotated
setAngle(45);
}
catch (IOException ex) {
}
}
public void setAngle(double angle) {
this.angle = angle;
// Using Affine transform we will calculate the new values
//x=vcos (theta)+wsin(theta)
//y=vcos(theta)+ wsin(theta)
double rads = Math.toRadians(angle); // calculating angle in radian
double sin = Math.abs(Math.sin(rads)), //calculating sin theta
cos = Math.abs(Math.cos(rads)); // calculating cos theta
int w = img.getWidth();
int h = img.getHeight();
int newWidth = (int) Math.floor(w * cos + h * sin); //using affine transform
int newHeight = (int) Math.floor(h * cos + w * sin);
rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = rotated.createGraphics(); //rotating planes.....
AffineTransform plane = new AffineTransform();
plane.translate((newWidth - w) / 2, (newHeight - h) / 2);
int x=w/2;
int y=h/2;
plane.rotate(Math.toRadians(45), x, y);
g2d.setTransform(plane);
g2d.drawImage(img, 0, 0, this);
}
public Dimension getPreferredSize() {
return new Dimension(800, // setting the window size
800);
}
protected void paintComponent(Graphics g) {
// super.paintComponent(g); no need for it
if (rotated != null) { // drawing image on 2 dimentional size surface
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - rotated.getWidth()) / 2;
int y = (getHeight() - rotated.getHeight()) / 2;
g2d.drawImage(rotated, x, y, this); // overriding the method......
}
}
}
}
What exactly don't you understand? plane.rotate(Math.toRadians(45), x, y); rotates the image around the center point clockwise by 45 degrees. plane.translate((newWidth - w) / 2, (newHeight - h) / 2); is the same as plane.translate(newWidth/2 - w/2, newHeight/2 - h/2); so it calculates the difference between the new center and the old center and moves the entire picture by that difference.
I should mention that AffineTransform applies the transformations in the opposite order.
So if you write this
plane.translate((newWidth - w) / 2, (newHeight - h) / 2);
int x=w/2;
int y=h/2;
plane.rotate(Math.toRadians(45), x, y);
Then every pixel of of the image is first rotated by 45 degrees clockwise and then shifted, not the other way around. It's done this way because the transformations are supposed to be applied to the coordinate system. Applying transformation A followed by tranformation B to the coordinate system is mathematically equivalent to applying B followed by A to the pixels of the image.
Related
I'm trying to make a cylinder using Java Graphics and the paintComponent() method.
The user of the application can select which shape they want (in this case it's a cylinder) and input the dimensions they want for the shape.
After they input the dimensions and click the submit button, another window will open with the image drawn on it.
My current issue is getting the shape made correctly. I'm currently trying to make two ovals and connect them using two lines. The base will be a red oval and everything else will have no color.
When you submit the dimensions for the cylinder, the sides are never the correct length or in the correct position on the Y-Axis. An example can be view here:
The dimensions for this cylinder: 200 height, 50 radius.
What the cylinder should look like:
300 height, 300 radius
I'll be working on adding the minimal version of the program for testing. However, for right now I'll be providing what the code is for the cylinder itself and the paintComponent() method.
Cylinder:
import java.awt.Color;
public class Cylinder extends Circle {
private int length;
public Cylinder(int radius, int length, Color color) {
super(radius, length, color);
this.length = length;
this.radius = radius;
}
public Cylinder(int newX, int newY, int newRadius, int newLength) {
super(newX, newY, newRadius);
length = newLength;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public int calcArea() {
return (int) Math.ceil( 2 * 3.14 * radius * radius + 2 * 3.14 * radius * length);
}
public int calcVolume() {
return (int) Math.ceil(3.14 * radius * radius * length);
}
public DrawFigure drawFigure() {
DrawFigure cylinder1 = new DrawFigure(4, getRadius(), length);
return cylinder1;
}
public String toString() {
return "Length = " + length + " " + super.toString();
}
}
DrawFigure (paintComponent is the last method):
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class DrawFigure extends JPanel {
int type;
int length, width, height, radius;
public DrawFigure() {
super();
type = 5;
}
public DrawFigure(int myType, int myWidth, int myLength, int myHeight) { // Box and Rectangle
super();
type = myType;
length = myLength;
width = myWidth;
height = myHeight;
}
public DrawFigure(int x, int y, int myType, int myWidth, int myLength, int myHeight) {
super();
type = myType;
length = myLength;
width = myWidth;
height = myHeight;
}
public DrawFigure(int myType, int myRadius, int myHeight) {
super();
type = myType;
radius = myRadius;
height = myHeight;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (type == 1) { // Draw Rectangle
} else if (type == 2) { // Draw Box
} else if(type == 3) { // Draw Circle
} else if(type == 4) { // Draw Cylinder
g.setColor(Color.BLACK);
g.drawOval(135, 65, radius, radius - radius / 2);
// Base
g.setColor(Color.RED);
g.fillOval(135, 65 + height, radius, radius / 2);
g.setColor(Color.BLACK);
g.drawLine(135, 65 + height + (height /4), 135, 135);
g.setColor(Color.BLACK);
g.drawLine(135 + radius, 65 + height + (height /4), 135 + radius, 135);
return;
}
}
}
Full code for the program:
Point.java https://pastebin.com/iVgN47e3
Lab6GUI.java https://pastebin.com/bKM790iQ
Rectangle.java https://pastebin.com/MdCrJYeA
Box.java https://pastebin.com/iZCZpUi7
Circle.java https://pastebin.com/aui1NgJi
Cylinder.java https://pastebin.com/fHDNmBXT
DrawFigure.java https://pastebin.com/z8t31put
LessThanOrEqualToZeroException.java https://pastebin.com/4ELEmsNX
LessThanOrGreaterThanException.java https://pastebin.com/1avRUudN
Okay, so drawing an oval extends from the x/y position, with a positive width/height, that would make the oval draw right/down from the x/y position, for example...
So, assuming we start at 0x0, this would mean that the lines would need to start at a y position of radius / 4, given that you're using radius for the width and radius / 2 for the height (I'm not going to mention how that is confusing). This will allow the lines to "appear" that they join the outer edge of the oval (and draw down)
The lines would then be height long. This means the bottom oval would then need to start at height - (radius / 4) ... okay, I had to go and double check this, but remember, the line ends at (radius / 4) + height, this also means that the cylinder is the height + (radius / 2) high in total.
🤪🤯
height=200, radius=50
height=300, radius=300
This prevents scenario where radius / 4 is greater than height, because that would just be a mess
Runnable example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new DrawPane(300, 300));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DrawPane extends JPanel {
int height, radius;
public DrawPane(int myRadius, int myHeight) {
super();
radius = myRadius;
height = myHeight;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - radius) / 2;
int y = (getHeight() - (height + (radius / 4))) / 2;
g2d.translate(x, y);
g2d.setColor(Color.LIGHT_GRAY);
g2d.drawRect(0, 0, radius, height + (radius / 4));
// Base
g2d.setColor(Color.RED);
g2d.fillOval(0, height - (radius / 4), radius, radius / 2);
g2d.setColor(Color.BLACK);
g2d.drawOval(0, 0, radius, radius / 2);
g2d.setColor(Color.BLACK);
g2d.drawLine(0, radius / 4, 0, height);
g2d.drawLine(radius, radius / 4, radius, height);
g2d.dispose();
}
}
}
Below is my current algorithm to align the rectangle (representing symbol) in the center of the canvas space (representing icon). It is only the algorithm I am interested in so ignore the rest of the code as it is merely for demonstration purposes as a visual aid.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class IconSymbol extends JFrame {
public IconSymbol(double iWH, double s, double w, double h) {
getContentPane().add(new Canvas((int)iWH, s, w, h));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize((int)iWH, (int)iWH);
setVisible(true);
}
public static void main(String arg[]) {
IconSymbol is = new IconSymbol(100, 0.9, 50, 50);
}
class Canvas extends JPanel {
// STIPULATED
double iconWH = 0;
double sScale = 0;
double sWidth = 0;
double sHeight = 0;
// CALCULATED
double padX = 0;
double padY = 0;
double xOffSet = 0;
double yOffSet = 0;
public Canvas(double iWH,double sS,double sW,double sH) {
this.iconWH = iWH;
this.sScale = sS;
this.sWidth = sW;
this.sHeight = sH;
}
public void paint(Graphics g) {
Graphics2D g2D = (Graphics2D) g;
g2D.setBackground(Color.WHITE);
g2D.setPaint(Color.BLUE);
Shape icon = new Rectangle.Double(0,0,(int)iconWH,(int)iconWH);
g2D.fill(icon);
g2D.setPaint(Color.BLACK);
int width = (int)iconWH / 10;
int height= (int)iconWH / 10;
for(int row=0;row<10;row++){
for(int col=0;col<10;col++){
g.drawRect(row*width,col*height,width,height);
}
}
Point off = algorithm();
g2D.setPaint(Color.RED);
Shape s = new Rectangle.Double(off.x,off.y,(int)sWidth,(int)sHeight);
AffineTransform tran = AffineTransform.getScaleInstance(sScale, sScale);
g2D.fill(tran.createTransformedShape(s));
}
public Point algorithm(){
// ALGORITHM WITH EXACT NEEDED PARAMETERS
padX = (sWidth - ((sWidth * sScale))) / 2;
padY = (sHeight - ((sHeight * sScale))) / 2;
xOffSet = padX + ((iconWH - (sWidth * sScale)) / 2);
yOffSet = padX + ((iconWH - (sHeight * sScale)) / 2);
Point point = new Point((int)xOffSet, (int)yOffSet);
return point;
}
}
}
The problem with your code is that the scale transform tran is scaling the calculated origin of the rectangle, off, as well as sWidth and sHeight. If you want to keep with your current scheme you need to apply the inverse of the scale transform to the calculated offset in your algorithm method:
public Point algorithm(){
// ALGORITHM WITH EXACT NEEDED PARAMETERS
xOffSet = ((iconWH - (sWidth * sScale)) / 2) / sScale;
yOffSet = ((iconWH - (sHeight * sScale)) / 2) / sScale;
Point point = new Point((int)xOffSet, (int)yOffSet);
return point;
}
Note that I removed padX and padY as they weren't required for calculating the offset.
I am trying to rotate a buffered image in java. Here is the code I am using:
public static BufferedImage rotate(BufferedImage bimg, double angle) {
int w = bimg.getWidth();
int h = bimg.getHeight();
Graphics2D graphic = bimg.createGraphics();
graphic.rotate(Math.toRadians(angle), w / 2, h / 2);
graphic.drawImage(bimg, null, 0, 0);
graphic.dispose();
return bimg;
}
I have looked a numerous stack overflow questions and answers on this topic and not been able to figure out why the image is chopped up the way it is when I try to rotate it. Here is an example showing a loaded image:
loaded image
After I click the rotate button which calls the above function with the buffered image and a 90.0 for the angle:
chopped up image
Can someone help me understand what is happening and how to fix it?
As always, the Internet to the rescue. So, this is some code which I hobbled together from other resources/post/blogs which will return a new image which is sized so it will contain the rotated image
public BufferedImage rotateImageByDegrees(BufferedImage img, double angle) {
double rads = Math.toRadians(angle);
double sin = Math.abs(Math.sin(rads)), cos = Math.abs(Math.cos(rads));
int w = img.getWidth();
int h = img.getHeight();
int newWidth = (int) Math.floor(w * cos + h * sin);
int newHeight = (int) Math.floor(h * cos + w * sin);
BufferedImage rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = rotated.createGraphics();
AffineTransform at = new AffineTransform();
at.translate((newWidth - w) / 2, (newHeight - h) / 2);
int x = w / 2;
int y = h / 2;
at.rotate(rads, x, y);
g2d.setTransform(at);
g2d.drawImage(img, 0, 0, this);
g2d.setColor(Color.RED);
g2d.drawRect(0, 0, newWidth - 1, newHeight - 1);
g2d.dispose();
return rotated;
}
Updated
So, using this PNG:
And this code...
package javaapplication1.pkg040;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private BufferedImage master;
private BufferedImage rotated;
public TestPane() {
try {
master = ImageIO.read(new File("/Volumes/Disk02/Dropbox/MegaTokyo/Miho_Small.png"));
rotated = rotateImageByDegrees(master, 0.0);
} catch (IOException ex) {
ex.printStackTrace();
}
Timer timer = new Timer(40, new ActionListener() {
private double angle = 0;
private double delta = 1.0;
#Override
public void actionPerformed(ActionEvent e) {
angle += delta;
rotated = rotateImageByDegrees(master, angle);
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return master == null
? new Dimension(200, 200)
: new Dimension(master.getWidth(), master.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (rotated != null) {
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - rotated.getWidth()) / 2;
int y = (getHeight() - rotated.getHeight()) / 2;
g2d.drawImage(rotated, x, y, this);
g2d.dispose();
}
}
public BufferedImage rotateImageByDegrees(BufferedImage img, double angle) {
double rads = Math.toRadians(angle);
double sin = Math.abs(Math.sin(rads)), cos = Math.abs(Math.cos(rads));
int w = img.getWidth();
int h = img.getHeight();
int newWidth = (int) Math.floor(w * cos + h * sin);
int newHeight = (int) Math.floor(h * cos + w * sin);
BufferedImage rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = rotated.createGraphics();
AffineTransform at = new AffineTransform();
at.translate((newWidth - w) / 2, (newHeight - h) / 2);
int x = w / 2;
int y = h / 2;
at.rotate(rads, x, y);
g2d.setTransform(at);
g2d.drawImage(img, 0, 0, this);
g2d.dispose();
return rotated;
}
}
}
I can generate something like...
You get jumbled image result because you are drawing the rotated image into the input image itself. Instead you need to create graphic from a new BufferedImage.
public static BufferedImage rotate(BufferedImage bimg, double angle) {
int w = bimg.getWidth();
int h = bimg.getHeight();
BufferedImage rotated = new BufferedImage(w, h, bimg.getType());
Graphics2D graphic = rotated.createGraphics();
graphic.rotate(Math.toRadians(angle), w/2, h/2);
graphic.drawImage(bimg, null, 0, 0);
graphic.dispose();
return rotated;
}
Note that if you want to avoid getting cropped corners, you need to adjust width & height of the output BufferedImage.
This code reads an image from a file, rotates it by a certain angle, and writes to another file. It works fine with png images with transparency:
public static void main(String[] args) throws IOException {
BufferedImage image = ImageIO.read(
Test.class.getResourceAsStream("/resources/image.png"));
BufferedImage rotated = rotateImage(image, 45);
ImageIO.write(rotated, "png",
new FileOutputStream("resources/rotated.png"));
}
private static BufferedImage rotateImage(BufferedImage buffImage, double angle) {
double radian = Math.toRadians(angle);
double sin = Math.abs(Math.sin(radian));
double cos = Math.abs(Math.cos(radian));
int width = buffImage.getWidth();
int height = buffImage.getHeight();
int nWidth = (int) Math.floor((double) width * cos + (double) height * sin);
int nHeight = (int) Math.floor((double) height * cos + (double) width * sin);
BufferedImage rotatedImage = new BufferedImage(
nWidth, nHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = rotatedImage.createGraphics();
graphics.setRenderingHint(
RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BICUBIC);
graphics.translate((nWidth - width) / 2, (nHeight - height) / 2);
// rotation around the center point
graphics.rotate(radian, (double) (width / 2), (double) (height / 2));
graphics.drawImage(buffImage, 0, 0, null);
graphics.dispose();
return rotatedImage;
}
image.png
rotated.png (45º) ⟳
rotated.png (-45º) ⟲
You'll have to account for resizing and new width and height for the output. See: https://stackoverflow.com/a/4787898/5420880
I drew a line that is at an angle based on a slider.
I am trying to make the line's end Y coordinate a certain number (let's say 300), even if it is at an angle.
Any ideas on how to do this? Here is the work on my line so far:
double angle = intAngle;
angle = angle * Math.PI / 180;
double length = 300;
graphics.setColor(Color.RED);
double startX = 300;
double startY = 100;
double endX = startX + length * Math.cos(angle);
double endY = startY + length * Math.sin(angle);
double end2X;
double end2Y;
double dblAngle;
double angle2;
int intAngle2;
double start2X = endX;
double start2Y = endY;
intAngle2 = 180 - intAngle;
angle2 = intAngle2;
angle2 = (angle2 * Math.PI / 180);
end2X = (start2X - length * Math.cos(angle2));
end2Y = (start2Y - length * Math.sin(angle2));
int intEndX = (int)endX;
int intEndY = (int)endY;
if(blnButton == true){
graphics.draw(new Line2D.Double(startX, startY, endX, endY));
graphics.draw(new Line2D.Double(start2X, start2Y, end2X, end2Y));
}
There's probably a simpler way, but basically, you can calculate two points on a circle based on the angle and the inverse of the angle (angle - 360)
With a circle with a radius of 150, this will give you a line of 300, for example
The red line is the line from the center of the circle to point on the circle represented by the given angel. The blue is the inverse. Each line is 150 pixels line, meaning together, they are 300 pixels in length.
This examples draws the separately, but realistically, they could be draw as a single line
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.Ellipse2D;
import java.awt.geom.Line2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new BorderLayout());
DrawPane drawPane = new DrawPane();
add(drawPane);
JSlider slider = new JSlider(0, 100);
add(slider, BorderLayout.SOUTH);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
drawPane.setAngleInDegrees(360d * (slider.getValue() / 100d));
}
});
slider.setValue(0);
}
}
public class DrawPane extends JPanel {
private double angle;
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
// Radius of the circle
double r = 150;
// Degrees to radians...
double radians = Math.toRadians(angle);
// The end point on the circle...
int endX = (int) Math.round(r * Math.cos(radians));
int endY = (int) Math.round(r * Math.sin(radians));
// The start point on the circle, 360 degress from the
// start angle
radians = Math.toRadians(angle - 360);
int startX = (int) Math.round(r * Math.cos(radians));
int startY = (int) Math.round(r * Math.sin(radians));
// Offset for the ellipse (center of the screen)
double x = (getWidth() / 2d) - r;
double y = (getWidth() / 2d) - r;
g2d.setColor(Color.LIGHT_GRAY);
g2d.draw(new Ellipse2D.Double(x, y, r * 2, r * 2));
// Center of the circle...
x = (getWidth() / 2d);
y = (getWidth() / 2d);
// One single line
//g2d.setColor(Color.BLACK);
//g2d.draw(new Line2D.Double(x - startX, y - startY, x + endX, y + endY));
g2d.setColor(Color.RED);
g2d.draw(new Line2D.Double(x, y, x - startX, y - startY));
g2d.setColor(Color.BLUE);
g2d.draw(new Line2D.Double(x, y, x + endX, y + endY));
g2d.dispose();
}
public void setAngleInDegrees(double value) {
if (angle != value) {
angle = Math.min(Math.max(value, 0), 360);
repaint();
}
}
}
}
or something along those lines...
I have a rectangle that rotates around it's middle and I have another rectangle that I want to connect to the upper right corner of the rotating rectangle. The problem is that I have no idea how to get the corner so that the second rectangle always will be stuck to that corner.
This is my sample code. Right now the second rectangle will be at the same place all the time which is not the result that I'm after.
package Test;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
class Test{
public static void main(String[] args){
new Test();
}
public Test(){
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new Graphic());
frame.setSize(1000,700);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
class Graphic extends JPanel{
private int x, y, windowW, windowH;
private double angle;
private Rectangle rect1, rect2;
private Path2D path;
private Timer timer;
private AffineTransform rotation;
public Graphic(){
windowW = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
windowH = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
path = new Path2D.Double();
rotation = new AffineTransform();
angle = 0;
x = windowW / 2;
y = windowH / 2;
timer = new Timer(100, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
angle += .1;
if(angle > 360) angle -= 360;
repaint();
}
});
timer.start();
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
rotation.setToTranslation(500, 200);
rotation.rotate(angle, 32, 32);
rect1 = new Rectangle(0, 0, 64, 64);
path = new Path2D.Double(rect1, rotation);
rect2 = new Rectangle(path.getBounds().x, path.getBounds().y, 10, 50);
g2d.fill(path);
g2d.fill(rect2);
}
}
Mathematical solution :)
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
rotation.setToTranslation(500, 200);
rotation.rotate(angle, 32, 32);
rect1 = new Rectangle(0, 0, 64, 64);
path = new Path2D.Double(rect1, rotation);
double r = 32.0 * Math.sqrt(2);
// (532, 232) - coordinates of rectangle center |
// you can change position of second rectangle by this V substraction (all you need to know is that the full circle corresponds to 2Pi)
int x2 = (int) Math.round(532 + r * Math.cos(angle - Math.PI / 4));
int y2 = (int) Math.round(232 + r * Math.sin(angle - Math.PI / 4));
rect2 = new Rectangle(x2, y2, 10, 50);
g2d.fill(path);
g2d.fill(rect2);
}
Of course, some constants should be class fields, not method variables.
I can't test this code to be sure but I believe it is the proper working code that you want
int hw = -width / 2;
int hh = -height / 2;
int cos = Math.cos( theta );
int sin = Math.sin( theta );
int x = hw * cos - hh * sin;
int y = hw * sin + hh * cos;
This will get you the top left corner based on the theta, rotation, of the square. To get the other corners you just use change the hw and hh values:
//top right corner
hw = width / 2
hh = -height / 2
//bottom right corner
hw = width / 2
hh = height / 2
//bottom left corer
hw = -width / 2
hh = height / 2
I hope this helps