So, I have a JPanel where I draw a triangle. My intent is to turn the triangle to arbitrary angles that the user chooses. Now in order to be able to rotate the triangle without having it looking cropped I need a JPanel bigger than the triangle. So to achieve all this, my paintcomponent looks like this:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Path2D p = new Path2D.Float();
p.moveTo(getWidth() / 4, getHeight() / 4);
p.lineTo(getWidth()-(getWidth() / 4), getHeight() / 2);
p.lineTo(getWidth() / 4, getHeight()-(getHeight() / 4));
p.closePath();
Graphics2D g2d = (Graphics2D) g.create();
g2d = (Graphics2D) g.create();
g2d.setColor(Color.GREEN);
g2d.rotate(Math.toRadians(rotationAngle), getWidth() / 2, getHeight() / 2);
g2d.fill(p);
g2d.dispose();
}
It works, but not how I would like it. Right now, it paints a green triangle over a transparent JPanel. The issue is that I want to keep the transparency on the JPanes when rotating the triangle. I know that I'm supposed to clear the contents of the JPanel if I want to redraw the JPanel and not end up with the old and new content, but all the responses I've seen ask to use clearRect which doesn't work here. clearRect will paint with the background color making the JPanel opaque. Can't I reinitialize the graphics component and draw again?
Right now, trying to set the background with something like
g2d.setBackground(new Color(0,0,0,0));
g2d.clearRect(0,0, getWidth(), getHeight());
Ends up just making a black background and the thing that seems more promising is maybe something like:
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, 0.3f));
But I don't know how to use the composite options and I keep making the triangle transparent and not the background
This should create the version with the clearRect() and the black background:
package clicknturn;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ClickNTurn extends JFrame {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
ClickNTurn ex = new ClickNTurn();
ex.setVisible(true);
}
});
}
public ClickNTurn() {
setTitle("Simple example");
setSize(500, 500);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
Entry tmpEntry = new Entry();
JPanel container = new JPanel();
container.setBackground(Color.GRAY);
container.setLayout(null);
this.add(container);
container.add(new Entry());
}
}
class Entry extends JPanel{
private int rotationAngle;
public Entry(){
this.setBounds(10,10, 200, 200);
this.setSize(200,200);
Entry me = this;
rotationAngle = 0;
setLayout(new GridBagLayout());
setOpaque(true);
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
rotationAngle += 10;
me.repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Path2D p = new Path2D.Float();
p.moveTo(getWidth() / 4, getHeight() / 4);
p.lineTo(getWidth()-(getWidth() / 4), getHeight() / 2);
p.lineTo(getWidth() / 4, getHeight()-(getHeight() / 4));
p.closePath();
Graphics2D g2d = (Graphics2D) g.create();
g2d = (Graphics2D) g.create();
g2d.setColor(Color.GREEN);
g2d.setBackground(new Color(0,0,0,0));
g2d.clearRect(0,0, getWidth(), getHeight());
g2d.setBackground(null);
g2d.rotate(Math.toRadians(rotationAngle), getWidth() / 2, getHeight() / 2);
g2d.fill(p);
g2d.dispose();
}
}
Ty!
Axel
Did you try setting the background color to "null"?
g2d.setBackground(null);
As I mentioned in my comment, set opaque on your drawing JPanel to false appears to fix your problem.
This was the program that I created several hours ago to test it:
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ClickNTurn extends JFrame {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
ClickNTurn ex = new ClickNTurn();
ex.setVisible(true);
}
});
}
public ClickNTurn() {
setTitle("Simple example");
setSize(500, 500);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
Entry tmpEntry = new Entry();
JPanel container = new JPanel();
container.setBackground(Color.GRAY);
container.setLayout(null);
this.add(container);
container.add(new Entry());
}
}
class Entry extends JPanel {
private int rotationAngle;
public Entry() {
this.setBounds(10, 10, 200, 200);
this.setSize(200, 200);
// !! Entry me = this;
rotationAngle = 0;
setLayout(new GridBagLayout());
//!! setOpaque(true);
setOpaque(false); //!!
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
rotationAngle += 10;
// !! me.repaint();
repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Path2D p = new Path2D.Float();
p.moveTo(getWidth() / 4, getHeight() / 4);
p.lineTo(getWidth() - (getWidth() / 4), getHeight() / 2);
p.lineTo(getWidth() / 4, getHeight() - (getHeight() / 4));
p.closePath();
Graphics2D g2d = (Graphics2D) g.create();
g2d = (Graphics2D) g.create();
g2d.setColor(Color.GREEN);
// g2d.setBackground(new Color(0, 0, 0, 0));
// g2d.clearRect(0, 0, getWidth(), getHeight());
g2d.setBackground(null);
g2d.rotate(Math.toRadians(rotationAngle), getWidth() / 2, getHeight() / 2);
g2d.fill(p);
g2d.dispose();
}
}
Use setOpaque and pass it false, this will make the component transparent, as well as let the paint engine know that it needs to take special care painting it, like clearing the Graphics context properly and painting beneath it.
There is nothing special you need to do in your code, simply continue painting as you normally would and the API will take care of the rest
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JPanel;
public class ClickNTurn extends JFrame {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
ClickNTurn ex = new ClickNTurn();
ex.setVisible(true);
}
});
}
public ClickNTurn() {
setTitle("Simple example");
setDefaultCloseOperation(EXIT_ON_CLOSE);
getContentPane().setBackground(Color.RED);
add(new Entry());
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
class Entry extends JPanel {
private int rotationAngle;
public Entry() {
Entry me = this;
rotationAngle = 0;
setLayout(new GridBagLayout());
setOpaque(false);
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
rotationAngle += 10;
me.repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Path2D p = new Path2D.Float();
p.moveTo(getWidth() / 4, getHeight() / 4);
p.lineTo(getWidth() - (getWidth() / 4), getHeight() / 2);
p.lineTo(getWidth() / 4, getHeight() - (getHeight() / 4));
p.closePath();
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.GREEN);
g2d.rotate(Math.toRadians(rotationAngle), getWidth() / 2, getHeight() / 2);
g2d.fill(p);
g2d.dispose();
}
}
}
Related
So i just learnt about affine transformation in java 2D and how each transformation behaves.So what i tried as a side project was to create a circle rotating around it's axis program,i tried translating first to the (0,0) then rotating by a degree then translating back to initial position,did that through 360 iterations with 1 degree increment but the circle still rotates out of that center points(although it goes back to its original point at last iteration).
here's what have done so far:
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
//Use of antialiasing to have nicer lines.
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
//The lines should have a thickness of 3.0 instead of 1.0.
BasicStroke bs = new BasicStroke(3.0f);
g2d.setStroke(bs);
//The GeneralPath to decribe the car.
//GeneralPath gp = new GeneralPath();
//Start at the lower front of the car.
g2d.setPaint(new Color(110, 100, 0));
RenderingHints rh = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
//Draw the car.
//g2d.fillOval(215, 135, 50, 50);
Shape s = new Ellipse2D.Double(160,160,40,40);
sustain(1000);
for(int i=0;i<360;i++) {
AffineTransform rotation = new AffineTransform();
rotation.setToRotation(Math.PI/180+i);
AffineTransform translate = new AffineTransform();
translate.setToTranslation(-160, -160);
AffineTransform translate2 = new AffineTransform();
translate2.setToTranslation(160, 160);
rotation.concatenate(translate);
translate2.concatenate(rotation);
clearWindow(g2d);
g2d.setPaint(new Color(110, 100, 0));
g2d.fill(translate2.createTransformedShape(s));
}
I've spent some time re-reading your question and looking over you code and I'm still unclear on
What it is you want to do and
What your problem is
But when has that ever stopped me from having a play 😉
Okay, so this has two circles (same shape) circling around a central point (translated) point.
Something to keep in mind is, transforms are accumulative, so you can see, between the second and third circle, I reset the transform (dispose of the graphics and take another snapshot) so my poor challenged brain doesn't get completely screwed up
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private int angle = 0;
public TestPane() {
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
angle += 1;
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(120, 120);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
//Use of antialiasing to have nicer lines.
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight());
g2d.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2);
RenderingHints rh = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Shape s = new Ellipse2D.Double(0, 0, 40, 40);
g2d.transform(AffineTransform.getTranslateInstance(40, 40));
g2d.setPaint(Color.RED);
g2d.draw(s);
g2d.transform(AffineTransform.getTranslateInstance(-30, -30));
g2d.transform(AffineTransform.getRotateInstance(Math.toRadians(angle), 50, 50));
g2d.setPaint(new Color(110, 100, 0));
g2d.drawRect(0, 0, 40, 40);
g2d.draw(s);
g2d.dispose();
g2d = (Graphics2D) g.create();
g2d.transform(AffineTransform.getTranslateInstance(40, 40));
g2d.transform(AffineTransform.getTranslateInstance(-20, -20));
g2d.transform(AffineTransform.getRotateInstance(Math.toRadians(angle / 2), 40, 40));
g2d.setPaint(Color.BLUE);
g2d.drawRect(0, 0, 40, 40);
g2d.draw(s);
}
}
}
I need it to rotate around its axis(have a circular motion in respect to its own center with out changing positions)
Okay, still not clear. If you want to rotate the object around it's centre point, but have it moving at the same time, then the order in which you apply your transformations is important.
For example, I'd translate it's position first, then rotate it, as it's easier to rotate about it's centre point without needing to calculate additional offsets
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private int angle = 0;
private Path2D path;
public TestPane() {
path = new Path2D.Double();
path.moveTo(20, 20);
path.lineTo(0, 20);
path.append(new Ellipse2D.Double(0, 0, 40, 40), false);
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
angle += 1;
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(120, 120);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
//Use of antialiasing to have nicer lines.
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
RenderingHints rh = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.transform(AffineTransform.getTranslateInstance(40, 40));
g2d.transform(AffineTransform.getRotateInstance(Math.toRadians(angle), 20, 20));
g2d.setPaint(Color.RED);
g2d.draw(path);
g2d.dispose();
}
}
}
And as an addition, you could also have a look at How to rotate an object around another moving object in java?
I am working on a class that can rotate a wheel around the center. The wheel is created using graphics2d, but I can not figure out exactly how to get the wheel to rotate around the center.
Currently, the wheel rotates, but not exactly about the origin.
My ultimate goal here is to create the wheel so that it is multicolored as well as a program around it, but my main concern here is getting the rotating wheel to work. If you could point me in the right direction I would be forever grateful!
Here is my current code:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.*;
public class RotateApp {
private static final int N = 3;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.setLayout(new GridLayout(N, N, N, N));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new RotatePanel());
frame.pack();
frame.setVisible(true);
System.out.println();
}
});
}
}
class RotatePanel extends JPanel implements ActionListener {
private static final int SIZE = 256;
private static double DELTA_THETA = Math.PI / 90;
private final Timer timer = new Timer(25, this);
private Image image = RotatableImage.getImage(SIZE);
private double dt = DELTA_THETA;
private double theta;
public RotatePanel() {
this.setBackground(Color.lightGray);
this.setPreferredSize(new Dimension(
image.getWidth(null), image.getHeight(null)));
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
image = RotatableImage.getImage(SIZE);
dt = -dt;
}
});
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.translate(this.getWidth() / 2, this.getHeight() / 2);
g2d.rotate(theta);
g2d.translate(-image.getWidth(this) / 2, -image.getHeight(this) / 2);
g2d.drawImage(image, 0, 0, null);
}
public void actionPerformed(ActionEvent e) {
theta += dt;
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(SIZE, SIZE);
}
}
class RotatableImage {
private static final Random r = new Random();
static public Image getImage(int size) {
BufferedImage bi = new BufferedImage(
size, size, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bi.createGraphics();
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint(Color.getHSBColor(r.nextFloat(), 1, 1));
g2d.setStroke(new BasicStroke(10.0f));
g2d.draw(new Line2D.Double(0, 100, 100, 100));
g2d.draw(new Line2D.Double(100, 100, 200, 100));
g2d.draw(new Line2D.Double(100, 0, 100, 100));
g2d.draw(new Line2D.Double(100, 100, 100, 200));
g2d.draw(new Line2D.Double(25, 25, 100, 100));
g2d.draw(new Line2D.Double(100, 100, 175, 175));
g2d.draw(new Line2D.Double(175, 25, 100, 100));
g2d.draw(new Line2D.Double(100, 100, 25, 175));
g2d.draw(new Ellipse2D.Double(0, 0, 200, 200));
g2d.dispose();
return bi;
}
}
You can use the Rotated Icon class to do the rotation for you so you don't have to worry about all the rotation logic and the rotation logic is in a reusable class.
An example of using this class would be:
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.util.*;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;
public class Rotation4 extends JPanel
{
private JLabel label;
private RotatedIcon rotated;
private int degrees;
public Rotation4(Image image)
{
setLayout( new GridBagLayout() );
Icon icon = new ImageIcon( image );
rotated = new RotatedIcon(icon, 0);
rotated.setCircularIcon(true);
label = new JLabel(rotated);
label.setOpaque(true);
label.setBackground(Color.RED);
add(label, new GridBagConstraints());
setDegrees( 0 );
}
public void setDegrees(int degrees)
{
this.degrees = degrees;
rotated.setDegrees( degrees );
label.revalidate();
label.repaint();
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
Image bi = RotatableImage.getImage(210);
final Rotation4 r = new Rotation4(bi);
final JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 360, 0);
slider.addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent e)
{
int value = slider.getValue();
r.setDegrees( value );
}
});
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JScrollPane(r));
f.add(slider, BorderLayout.SOUTH);
f.setSize(400, 400);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}
static class RotatableImage
{
private static final Random r = new Random();
static public Image getImage(int size)
{
BufferedImage bi = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bi.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint(Color.getHSBColor(r.nextFloat(), 1, 1));
g2d.setStroke(new BasicStroke(10.0f));
g2d.draw(new Line2D.Double(5, 105, 205, 105));
g2d.draw(new Line2D.Double(105, 5, 105, 205));
g2d.draw(new Line2D.Double(35, 35, 175, 175));
g2d.draw(new Line2D.Double(175, 35, 35, 175));
g2d.draw(new Ellipse2D.Double(5, 5, 199, 199));
g2d.setColor(Color.BLACK);
g2d.fillOval(100, 100, 10, 10);
g2d.dispose();
return bi;
}
}
}
Note I also had to make changes with your image and your painting. These changes will need to be made whether you use the RotatedIcon or do the rotation code yourself:
The image size was changed to 210. This is because your stroke size is 10, so you need to account for the extra pixels in the circle outline.
You need to change the original of the circle by half the stroke size. So in this case the origin becomes (5, 5).
The size of the oval needs to be changed to 199. This is because of the way the outline of the oval is painted. 1 extra pixel is needed for the outline. If you leave the size at 200 then 1 pixel of the outline will be lost. This is not very noticeable when using a stroke size of 10, but if you use a size of 1, then the outline will be missing at the right and bottom edges.
The locations of your lines needs to be changes. you don't want the line right to the edge of the circle because then you will get a flat line at the edge instead of the rounded line. So I started the line 5 pixels from the start and ended it 5 pixels from the end.
Ok, with little modification and fewer "spokes", I got your wheel rotate centric (1.) and multicolor (2.):
UPDATE on 1.) To make centric rotation in your (original) code just chage SIZEto 200!
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.*;
public class RotateApp {
private static final int N = 3;
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame frame = new JFrame();
frame.setLayout(new GridLayout(N, N, N, N));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new RotatePanel());
frame.pack();
frame.setVisible(true);
System.out.println();
});
}
}
class RotatePanel extends JPanel implements ActionListener {
private static final int SIZE = 256;
private static final double DELTA_THETA = Math.PI / 90;
private final Timer timer = new Timer(25, this);
private Image image = RotatableImage.getImage(SIZE);
private double dt = DELTA_THETA;
private double theta;
public RotatePanel() {
this.setBackground(Color.lightGray);
this.setPreferredSize(new Dimension(SIZE, SIZE));
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
dt = -dt;
image = RotatableImage.getImage(SIZE);
}
});
timer.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.rotate(theta,128,128);
g2d.drawImage(image, 0, 0, null);
}
#Override
public void actionPerformed(ActionEvent e) {
theta += dt;
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(SIZE, SIZE);
}
}
class RotatableImage {
private static final Random r = new Random();
static public Image getImage(int size) {
BufferedImage bi = new BufferedImage(
size, size, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bi.createGraphics();
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
final Color c1 = Color.getHSBColor(r.nextFloat(), 1, 1);
final Color c2 = Color.getHSBColor(r.nextFloat(), 1, 1);
g2d.setPaint(c1);
g2d.setStroke(new BasicStroke(10.0f));
g2d.draw(new Line2D.Double(0, size/2, size, size/2));
g2d.setPaint(c2);
g2d.draw(new Line2D.Double(size/2, 0, size/2, size));
g2d.setPaint(c1);
g2d.draw(new Ellipse2D.Double(0, 0, size, size));
g2d.dispose();
return bi;
}
}
Explanation:
So the "wobble" in your solution came from the fact, that you sized the image and the container/panel 256x256, but "based" your wheel layout on 200x200 only. I fixed all dimensions, and drew a correct cross, the g2d.rotate(theta,128,128); (!) relates then to the center.
Multi-color(easy): You can invoke setPaint() between each shape! ;)
I want to set up a mathematical (where y grows up not down) coordinate space from (-1, -1) to (+1, +1) and have it fit in the window regardless of the window size.
I am using an anonymous JComponent subclass in Java SE 7 and casting the incoming Graphics in paintComponent to Graphics2D and then drawing on the Graphics2D
But the Graphics2D is set to a computer coordinate space that changes with the size of the window. How to get it to rescale according to window size and have Y go upwards? The following program should show a dark square in upper right quadrant.
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class G {
public static void main (String [] args) {
JFrame frame = new JFrame(G.class.getCanonicalName());
frame.setUndecorated(true);
JComponent component = new JComponent() {
private static final long serialVersionUID = 1L;
#Override
protected void paintComponent (Graphics g) {
super.paintComponent(g);
paint2D((Graphics2D)g);
}
protected void paint2D (Graphics2D g2) {
g2.draw(new Rectangle2D.Double(0.1, 0.1, 0.9, 0.9));
}
};
frame.add(component);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.setVisible(true);
}
}
Setup the coordinate system how you want, using transform() and translate(). So:
you want the origin to be at (0, height); bottom left.
then you want to flip the Y axis.
Example code:
AffineTransform tform = AffineTransform.getTranslateInstance( 0, height);
tform.scale( 1, -1);
g2.setTransform( tform);
[My edited version]:
public static void main (String [] args) {
JFrame frame = new JFrame( G2dTransform_Question.class.getCanonicalName());
JComponent component = new JComponent() {
private static final long serialVersionUID = 1L;
#Override
protected void paintComponent (Graphics g) {
super.paintComponent(g);
paint2D((Graphics2D)g);
}
protected void paint2D (Graphics2D g2) {
AffineTransform tform = AffineTransform.getTranslateInstance( 0, getHeight());
tform.scale( getWidth(), -getHeight()); // NOTE -- to make 1.0 'full width'.
g2.setTransform( tform);
g2.setColor( Color.BLUE); // NOTE -- so we can *see* something.
g2.fill( new Rectangle2D.Double(0.1, 0.1, 0.8, 0.8)); // NOTE -- 'fill' works better than 'draw'.
}
};
frame.setLayout( new BorderLayout()); // NOTE -- make the component size to frame.
frame.add( component, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.setVisible(true);
}
[Hovercraft's version]: Thanks Hover!
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class G {
public static final int PREF_W = 400;
public static final int PREF_H = PREF_W;
public static void main (String [] args) {
JFrame frame = new JFrame(G.class.getCanonicalName());
frame.setUndecorated(true);
JComponent component = new JComponent() {
private static final long serialVersionUID = 1L;
#Override
protected void paintComponent (Graphics g) {
super.paintComponent(g);
AffineTransform tform = AffineTransform.getTranslateInstance( 0, getHeight());
tform.scale( 1, -1);
Graphics2D g2 = (Graphics2D) g.create();
g2.setTransform( tform);
paint2D(g2);
g2.dispose();
}
protected void paint2D (Graphics2D g2) {
g2.draw(new Rectangle2D.Double(10, 10, 20, 30));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
};
frame.add(component);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}
I am writing an application which has a JLayeredPane (call it layers) containing two JPanels in different layers. I override the paintComponent method of the JPanel at the bottom (call it grid_panel) so it paints a grid, and the the paintComponent method of the one at the top (call it circuit_panel) so it paints a circuit.
Here's a summary of the structure:
layers -
|-circuit_panel (on top)
|-grid_panel (at bottom)
I want the grid_panel to stay static, ie, not to do any repaint (except the initial one) since it does not change.
The trouble is, whenever I call circuit_panel.repaint(), grid_panel gets repainted as well! This is a definitely not efficient.
I think this is due to the eager painting behavior of JLayeredPane. Is there a way to disable this feature in JLayeredPane?
In case you're interested to see the above effect, I've written a small demo program:
public class Test2 extends JFrame {
public Test2() {
JLayeredPane layers = new JLayeredPane();
layers.setPreferredSize(new Dimension(600, 400));
MyPanel1 myPanel1 = new MyPanel1();
MyPanel2 myPanel2 = new MyPanel2();
myPanel1.setSize(600, 400);
myPanel2.setSize(600, 400);
myPanel1.setOpaque(false);
myPanel2.setOpaque(false);
myPanel2.addMouseListener(new MyMouseListener(myPanel2));
layers.add(myPanel1, new Integer(100)); // At bottom
layers.add(myPanel2, new Integer(101)); // On top
this.getContentPane().add(layers, BorderLayout.CENTER);
this.setSize(600, 400);
}
class MyPanel1 extends JPanel {
Color getRandomColor() {
int r = (int) (256 * Math.random());
int g = (int) (256 * Math.random());
int b = (int) (256 * Math.random());
return new Color(r, g, b);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(getRandomColor());
g2d.fillRoundRect(30, 30, 60, 60, 5, 5);
}
}
class MyPanel2 extends JPanel {
Color getRandomColor() {
int r = (int) (256 * Math.random());
int g = (int) (256 * Math.random());
int b = (int) (256 * Math.random());
return new Color(r, g, b);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(getRandomColor());
g2d.fillRoundRect(45, 45, 75, 75, 5, 5);
}
}
class MyMouseListener extends MouseAdapter {
JPanel panel;
MyMouseListener(JPanel panel) {
this.panel = panel;
}
#Override
public void mouseClicked(MouseEvent e) {
panel.repaint();
}
}
/**
* #param args
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
(new Test2()).setVisible(true);
}
});
}
}
As you found, a BufferedImage is an effective way to cache complex content for efficient rendering; CellTest is an example. A flyweight renderer, shown here, is another approach. Finally, I've re-factored your instructive example in a way that may make experimentation easier.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
/** #see https://stackoverflow.com/q/9625495/230513 */
public class LayerDemo extends JFrame {
private static final Dimension d = new Dimension(320, 240);
public LayerDemo() {
JLayeredPane layers = new JLayeredPane();
layers.setPreferredSize(d);
layers.add(new LayerPanel(1 * d.height / 8), 100);
layers.add(new LayerPanel(2 * d.height / 8), 101);
layers.add(new LayerPanel(3 * d.height / 8), 102);
this.add(layers, BorderLayout.CENTER);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.pack();
this.setLocationByPlatform(true);
}
private static class LayerPanel extends JPanel {
private static final Random r = new Random();
private int n;
private Color color = new Color(r.nextInt());
public LayerPanel(int n) {
this.n = n;
this.setOpaque(false);
this.setBounds(n, n, d.width / 2, d.height / 2);
this.addMouseListener(new MouseHandler(this));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(color);
g2d.fillRoundRect(0, 0, getWidth(), getHeight(), 16, 16);
g2d.setColor(Color.black);
g2d.drawString(String.valueOf(n), 5, getHeight() - 5);
}
private void update() {
color = new Color(r.nextInt());
repaint();
}
}
private static class MouseHandler extends MouseAdapter {
LayerPanel panel;
MouseHandler(LayerPanel panel) {
this.panel = panel;
}
#Override
public void mouseClicked(MouseEvent e) {
panel.update();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
(new LayerDemo()).setVisible(true);
}
});
}
}
Is there a way to make a JLabel's text stretch to 100% height? I need the text to update when the component's size changes as well.
I saw some solution that could work; It involved calculating and setting the font size so it appears the right height. I would have also have to add listeners for when the height changed to make it respond and I do not know exactly where I should do that.
I am hoping for a better solution with layout managers, but couldn't find anything.
Any ideas?
In the approach shown below, the desired text is imaged using TextLayout using a suitably large Font size and scaled to fill the component. There's a related example here.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JLabel;
/** #see https://stackoverflow.com/questions/8281886 */
public class LayoutTest extends JLabel {
private static final int SIZE = 256;
private BufferedImage image;
private LayoutTest(String string) {
super(string);
image = createImage(super.getText());
}
#Override
public void setText(String text) {
super.setText(text);
image = createImage(super.getText());
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(image.getWidth() / 2, image.getHeight() / 2);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
}
private BufferedImage createImage(String label) {
Font font = new Font(Font.SERIF, Font.PLAIN, SIZE);
FontRenderContext frc = new FontRenderContext(null, true, true);
TextLayout layout = new TextLayout(label, font, frc);
Rectangle r = layout.getPixelBounds(null, 0, 0);
System.out.println(r);
BufferedImage bi = new BufferedImage(
r.width + 1, r.height + 1, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = (Graphics2D) bi.getGraphics();
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(getBackground());
g2d.fillRect(0, 0, bi.getWidth(), bi.getHeight());
g2d.setColor(getForeground());
layout.draw(g2d, 0, -r.y);
g2d.dispose();
return bi;
}
private static void display() {
JFrame f = new JFrame("LayoutTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new LayoutTest("Sample"));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
display();
}
});
}
}