Given the following :
public class NavigationCanvas extends Canvas implements MouseListener,MouseMotionListener,KeyListener {
public void paint(Graphics g)
{
// some code
// more
// ...
g.setColor(Color.black);
// drawing each Line
for (int i=0; i<length; i++)
{
Line2D currLine = m_lines.get(i);
g.drawLine((int)currLine.getX1(),(int)currLine.getY1(),
(int)currLine.getX2(),(int)currLine.getY2());
g.drawLine((int)currLine.getX1()+1,(int)currLine.getY1()+1
,(int)currLine.getX2()+1,(int)currLine.getY2()+1);
g.drawLine((int)currLine.getX1()+2,(int)currLine.getY1()+2
,(int)currLine.getX2()+2,(int)currLine.getY2()+2);
}
}
...
}
When I draw the lines of currLine I get this :
As you can see , I made 3 calls to drawline() , to make it more bold ,but it still doesn't quite
as I wanted .
How can I draw one bold line ?
Graphics2D#setStroke controls the style of line that is painted. BasicStroke is the default implementation of Stroke and has a number of parameters, the one you're most interested in is the width.
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestStroke {
public static void main(String[] args) {
new TestStroke();
}
public TestStroke() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int width = getWidth();
int height = getHeight();
int xDif = width / 4;
int yDif = height / 4;
g2d.setStroke(new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2d.drawLine(xDif, yDif, width - xDif, yDif);
g2d.setStroke(new BasicStroke(2, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2d.drawLine(width - xDif, yDif, width - xDif, height - yDif);
g2d.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2d.drawLine(width - xDif, height - yDif, xDif, height - yDif);
g2d.setStroke(new BasicStroke(4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2d.drawLine(xDif, height - yDif, xDif, yDif);
g2d.dispose();
}
}
}
Have a look at Stroking and filling Graphics Primitives for more details
Use the setStroke() method located in the Graphics library: http://docs.oracle.com/javase/1.5.0/docs/api/java/awt/Graphics2D.html
setStroke(Stroke s)
Sets the Stroke for the Graphics2D context.
It takes a Stroke object, http://docs.oracle.com/javase/1.5.0/docs/api/java/awt/Stroke.html
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'm trying to create a drawing on BufferedImage and then copy in onto JPanel.
When I draw directly on JPanel quality of the picture is v.good but when using intermediate BufferedImage quality / resolution is visibly reduced.
I've checked that with zoom option from OSX's Accessibility panel.
I'm developing on MacBook Pro Retina.
Is there some sort of automated scaling happening?
What am I doing wrong with BufferedImage?
Here's the code demonstrating the problem
package com.sample.gui;
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.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class QualityProblem {
private static final double DOT_SIZE = 4;
public static void main(String[] args) {
JFrame frame = new JFrame("ChartPanel demo");
frame.setBackground(Color.WHITE);
// JPanel draw = new DrawingOK();
JPanel draw = new DrawingUgly();
draw.setBackground(Color.BLACK);
frame.getContentPane().add(draw, BorderLayout.CENTER);
frame.setSize(new Dimension(1200, 900));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static class DrawingOK extends JPanel {
private static final long serialVersionUID = 1L;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2draw = (Graphics2D) g.create();
try {
g2draw.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2draw.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2draw.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
Ellipse2D.Double e = new Ellipse2D.Double(50, 50, DOT_SIZE, DOT_SIZE);
g2draw.setColor(Color.YELLOW);
g2draw.fill(e);
} finally {
g2draw.dispose();
}
}
}
private static class DrawingUgly extends JPanel {
private static final long serialVersionUID = 1L;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Dimension size = getParent().getSize();
BufferedImage image = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_RGB);
Graphics2D ig = image.createGraphics();
ig.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
ig.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
ig.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
Graphics2D g2draw = (Graphics2D) g.create();
try {
Ellipse2D.Double e = new Ellipse2D.Double(50, 50, DOT_SIZE, DOT_SIZE);
ig.setColor(Color.YELLOW);
ig.fill(e);
g2draw.drawImage(image, 0, 0, null);
} finally {
ig.dispose();
g2draw.dispose();
}
}
}
}
Edited:
Added images with 4 pixel dot and 50D both zoomed in.
Ugly one comes from BufferedImage copied onto screen's Graphics
I fixed up your drawing code.
Here's the ugly GUI.
I moved the sizing of the panel to the panel constructor. Setting the frame size includes the borders. Setting the panel size gives you the drawing area you want.
I moved the black background painting to the paintComponent method. You might as well do all the painting in one place.
I cleaned up your drawing code. You don't need to make a copy of the paintComponent graphics instance to get Graphics2D.
I made the circle bigger so you could see the sharpness. I moved the origin to the center of the circle, and turned the DOT_SIZE into a radius.
Here's the code.
package com.ggl.testing;
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.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class QualityProblem implements Runnable {
private static final double DOT_SIZE = 50D;
public static void main(String[] args) {
SwingUtilities.invokeLater(new QualityProblem());
}
#Override
public void run() {
JFrame frame = new JFrame("ChartPanel demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBackground(Color.WHITE);
// JPanel draw = new DrawingOK();
JPanel draw = new DrawingUgly();
frame.getContentPane().add(draw, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
private class DrawingOK extends JPanel {
private static final long serialVersionUID = 1L;
public DrawingOK() {
this.setPreferredSize(new Dimension(600, 400));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2draw = (Graphics2D) g;
g2draw.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2draw.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2draw.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_PURE);
g2draw.setColor(Color.BLACK);
g2draw.fillRect(0, 0, getWidth(), getHeight());
Ellipse2D.Double e = new Ellipse2D.Double(300D - DOT_SIZE,
200D - DOT_SIZE, DOT_SIZE + DOT_SIZE, DOT_SIZE + DOT_SIZE);
g2draw.setColor(Color.YELLOW);
g2draw.fill(e);
}
}
private class DrawingUgly extends JPanel {
private static final long serialVersionUID = 1L;
public DrawingUgly() {
this.setPreferredSize(new Dimension(600, 400));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
BufferedImage image = new BufferedImage(getWidth(), getHeight(),
BufferedImage.TYPE_INT_RGB);
Graphics2D ig = image.createGraphics();
ig.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
ig.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
ig.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_PURE);
ig.setColor(Color.BLACK);
ig.fillRect(0, 0, getWidth(), getHeight());
Ellipse2D.Double e = new Ellipse2D.Double(300D - DOT_SIZE,
200D - DOT_SIZE, DOT_SIZE + DOT_SIZE, DOT_SIZE + DOT_SIZE);
ig.setColor(Color.YELLOW);
ig.fill(e);
ig.dispose();
g.drawImage(image, 0, 0, this);
}
}
}
This is simply because in one case, you're drawing on a hardware-supported surface that says it's 640x480 but rendering is done at 2x (or whatever scaling factor of your display) resolution. In the case of BufferedImage you're drawing onto a literal 640x480 pixel buffer. Obviously, that will look worse.
I think that the image and panel are using different rendering hints on OS X for hints you've not explicitly set. Copy/paste the textual output of this code back into the question.
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class QualityProblem {
private static final double DOT_SIZE = 40;
public static void main(String[] args) {
JFrame frame = new JFrame("ChartPanel demo");
frame.setLayout(new GridLayout(0, 1));
frame.getContentPane().add(new DrawingUgly());
frame.getContentPane().add(new DrawingOK());
frame.setSize(new Dimension(400, 300));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static class DrawingOK extends JPanel {
private static final long serialVersionUID = 1L;
DrawingOK() {
setBackground(Color.GREEN);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2draw = (Graphics2D) g.create();
System.out.println("Panel Rendering Hints:");
printRenderingHints(g2draw);
try {
g2draw.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2draw.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2draw.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
Ellipse2D.Double e = new Ellipse2D.Double(50, 50, DOT_SIZE, DOT_SIZE);
g2draw.setColor(Color.YELLOW);
g2draw.fill(e);
} finally {
g2draw.dispose();
}
}
}
private static class DrawingUgly extends JPanel {
private static final long serialVersionUID = 1L;
DrawingUgly() {
setBackground(Color.RED);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Dimension size = getParent().getSize();
BufferedImage image = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_ARGB);
Graphics2D ig = image.createGraphics();
System.out.println("Image Rendering Hints:");
printRenderingHints(ig);
ig.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
ig.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
ig.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
Graphics2D g2draw = (Graphics2D) g.create();
try {
Ellipse2D.Double e = new Ellipse2D.Double(50, 50, DOT_SIZE, DOT_SIZE);
ig.setColor(Color.YELLOW);
ig.fill(e);
g2draw.drawImage(image, 0, 0, null);
} finally {
ig.dispose();
g2draw.dispose();
}
}
}
private static void printRenderingHints(Graphics2D g) {
RenderingHints renderingHints = g.getRenderingHints();
RenderingHints.Key[] renderHintsKeys = {
RenderingHints.KEY_ALPHA_INTERPOLATION,
RenderingHints.KEY_ANTIALIASING,
RenderingHints.KEY_COLOR_RENDERING,
RenderingHints.KEY_DITHERING,
RenderingHints.KEY_FRACTIONALMETRICS,
RenderingHints.KEY_INTERPOLATION,
RenderingHints.KEY_RENDERING,
RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.KEY_TEXT_LCD_CONTRAST
};
for (RenderingHints.Key key : renderHintsKeys) {
Object o = renderingHints.get(key);
String value = o==null ? "null" : o.toString();
System.out.println(key + " \t" + value);
}
}
}
Note that on Windows it produces an identical list of values.
HaraldK in one comments below question gave really good advice. BufferedImage size needs to be multiplied by 2, Graphics2D for that image must be set with scale 2 and target Graphics2D (of the screen device) needs to be scaled with 0.5.
With those settings both circles look exactly the same when zoomed in.
Bellow complete, modified DrawingUgly class.
private static class DrawingUgly extends JPanel {
private static final long serialVersionUID = 1L;
public DrawingUgly() {
this.setPreferredSize(new Dimension(600, 25));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2draw = (Graphics2D) g.create();
double scale = 2;
BufferedImage image = new BufferedImage((int) (getWidth() * scale), (int) (getHeight() * scale), BufferedImage.TYPE_INT_RGB);
Graphics2D ig = image.createGraphics();
ig.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
ig.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
ig.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
ig.scale(scale, scale);
ig.setColor(Color.BLACK);
ig.fillRect(0, 0, getWidth(), getHeight());
Ellipse2D.Double e = new Ellipse2D.Double(10, 10, DOT_SIZE, DOT_SIZE);
ig.setColor(Color.YELLOW);
ig.fill(e);
ig.dispose();
g2draw.scale(1.0d / scale, 1.0d / scale);
g2draw.drawImage(image, 0, 0, this);
}
}
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();
}
}
}
I know how to draw a rounded rectangle but I want to define roundness for each corner separately and draw something like the image below :
There's probably a few ways to achieve this, but the easiest I can think of would be to, as Andrew has already hinted, would be to define your own Shape
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class SimpleShape {
public static void main(String[] args) {
new SimpleShape();
}
public SimpleShape() {
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 RightEnd rightEnd;
public TestPane() {
rightEnd = new RightEnd(100, 100, 40);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = (getWidth() - 100) / 2;
int y = (getHeight()- 100) / 2;
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.translate(x, y);
g2d.fill(rightEnd);
g2d.dispose();
}
}
public class RightEnd extends Path2D.Float {
public RightEnd(float width, float height, float radius) {
moveTo(0, 0);
lineTo(width - radius, 0);
curveTo(width, 0, width, 0, width, radius);
lineTo(width, height - radius);
curveTo(width, height, width, height, width - radius, height);
lineTo(0, height);
closePath();
}
}
}
I am drawing 4 stacked rectangles, rotating the canvas about a point and then drawing that same shape again but the stacked rectangles aren't remaining completely aligned.
Here's a screenshot:
As you can see, the first, unrotated block is perfect but the following ones are misaligned. Why is this happening and how can I prevent it?
SSCE
//Just import everything to keep it short and sweet
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class AlignmentIssue extends JComponent {
#Override
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
double angle = Math.toRadians(30);
int numRects = (int)Math.floor(2.0 * Math.PI / angle);
Rectangle rect = new Rectangle(0, 100, 20, 25);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.translate(getWidth() / 2, getHeight() / 2);
for(int i = 0; i < numRects; ++i) {
AffineTransform transform = g2d.getTransform();
for(int n = 0; n < 3; ++n) {
g2d.draw(rect);
g2d.translate(0, rect.height);
}
g2d.setTransform(transform);
g2d.rotate(angle);
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Alignment Issue");
frame.add(new AlignmentIssue());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(800, 600));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
You need more precise stroke control:
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
The default may let the geometry vary a bit, depending on the implementation. Pure tries to keep subpixel accuracy.