java, rotate BufferedImage with mouse angle - java

I have been searching all over the internet for a simple way to just rotate a sprite following an angle.
The objective is to have a weapon sprite following the mouse by rotating at the centre of the screen (Top-down shooter in Java). I have tried different things:
NB: The render(Graphics g) function is inside my Gun.java class and uses g, the Graphics element I use to paint on the canvas of the game. The image is the BufferedImage containing the original sprite. And reticle.getAngle() is giving the angle made by the mouse considering the centre of the screen as the origin of the frame.
Attempt 1
public void render(Graphics g) {
// create a new BufferedImage with the image of the gun on it
BufferedImage rotatedImage = new BufferedImage(image.getWidth(), image.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics gRotatedImage = rotatedImage.getGraphics();
gRotatedImage.drawImage(image, 0, 0, null);
// rotate this gun image in the direction of shoot
private AffineTransform at = new AffineTransform();
at.rotate(reticle.getAngle() + Math.PI,
rotatedImage.getWidth()/2, rotatedImage.getHeight()/2);
AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
rotatedImage = op.filter(rotatedImage, null);
// finally display the rotated version of the gun image
g.drawImage(rotatedImage,
(int)(handler.getDisplay().getWidth()/2 - rotatedImage.getWidth()/2),
(int)(handler.getDisplay().getHeight()/2 - rotatedImage.getHeight()/2),
rotatedImage.getWidth(), rotatedImage.getHeight(), null);
}
With this solution, from java2s.com, I end up with the sprite being displayed at the centre and rotating but more like a helicopter... It keeps rotating not following the mouse.
I also tested all the solutions from the related StackOverflow question. This time, I get the weapon being displayed at the top left corner following the mouse, but I can't find a way to place it at the centre of the screen. I tried translations but then the sprite image rotates considering the top left corner as the centre.
Attempt 2
public void render(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
double rotation = 0f;
int width = image.getWidth() - 1;
int height = image.getHeight() - 1;
rotation = reticle.getAngle();
rotation = Math.toDegrees(rotation) + 180;
g2d.rotate(Math.toRadians(rotation), width / 2, height / 2);
// g2d.translate(handler.getDisplay().getWidth()/2, handler.getDisplay().getHeight()/2);
g2d.drawImage(image, 0, 0, null);
int x = width / 2;
int y = height / 2;
g2d.setStroke(new BasicStroke(3));
g2d.setColor(Color.RED);
g2d.drawLine(x, y, x, y - height / 4);
g2d.dispose();
}
I just would like to rotate my sprite every tick of the game by the angle provided by reticle.getAngle() which I know is good. I feel really lost on how to use Graphics2D or AffineTransform to perform rotation. Can someone provide an example on how to rotate a sprite following the mouse and then display it at the centre of the screen?
What is the best way to rotate an image which we then want to display at the centre of the screen?

I don't know if this helps, but I wrote this example program, which has a rectangle (works the same as an image really...) at the center of the screen which rotates following your mouse. The important stuff is in the paintComponent method, pretty much everything else is setup.
package main;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class GPanel extends JPanel{
private int width, height;
private Timer timer;
private int mouseX, mouseY;
public static void main(String[] args) {
JFrame f = new JFrame();
GPanel gp = new GPanel(400, 400);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(gp,BorderLayout.CENTER);
f.pack();
f.setVisible(true);
}
public GPanel(int width, int height) {
mouseX = 0;
mouseY = 0;
this.width = width;
this.height = height;
this.setPreferredSize(new Dimension(width, height));
timer = new Timer(17 , new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TO DO
repaint();
}
});
addMouseMotionListener();
timer.start();
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g.setColor(new Color(255, 255, 255));
g2.fillRect(0, 0, width, height);
g2.setColor(new Color(0, 0, 0));
g2.translate(width / 2, height / 2);
double x = mouseX - width / 2d;
double y = mouseY - height / 2d;
double theta = Math.atan2(x, y);
g2.rotate(-theta);
g2.translate(-20, 0);
g2.fillRect(0, 0, 40, 100);
}
private void addMouseMotionListener() {
this.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
mouseX = e.getX();
mouseY = e.getY();
}
#Override
public void mouseDragged(MouseEvent e) {
if(SwingUtilities.isLeftMouseButton(e)) {
//TO DO
} else if(SwingUtilities.isRightMouseButton(e)) {
}
repaint();
}
});
}
}

What I would do is create an affine transformation for the sprite. I imagine a sprite, img should be at (cx, cy) and the angle it is rotated should be theta.
Graphics2D g2d = (Graphics2D)g;
AffineTransform at = new AffineTransform();
//translate the center to be at cx, cy.
at.translate(cx - img.getWidth()/2.0, cy - img.getHeight()/2.0);
//rotate about the center of the sprite.
at.rotate(theta, img.getWidth()/2.0, img.getHeight()/2.0);
g2d.drawImage(img, at, this);
I tested it out with this example, where you can click on the JPanel and center the sprite. The sprite follows the mouse cursor around when you move it.
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.geom.*;
public class RotateArrow{
int cx = 0;
int cy = 0;
double theta = 0;
public void startGui(){
JFrame frame = new JFrame("arrow");
BufferedImage img = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB);
Graphics g = img.getGraphics();
g.setColor(Color.RED);
g.drawLine(0, 32, 64, 32);
g.drawLine(48, 0, 48, 64);
g.dispose();
JPanel panel = new JPanel(){
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
AffineTransform at = new AffineTransform();
//rotate about center of image.
at.translate(cx - img.getWidth()/2.0, cy - img.getHeight()/2.0);
at.rotate(theta, img.getWidth()/2.0, img.getHeight()/2.0);
g2d.drawImage(img, at, this);
}
};
panel.addMouseListener( new MouseAdapter(){
#Override
public void mousePressed(MouseEvent evt){
cx = evt.getX();
cy = evt.getY();
panel.repaint();
}
} );
panel.addMouseMotionListener( new MouseAdapter(){
#Override
public void mouseMoved(MouseEvent evt){
double dx = evt.getX() - cx;
double dy = evt.getY() - cy;
if(dx != 0 || cy != 0){
theta = Math.atan2(dy, dx);
panel.repaint();
}
}
} );
frame.setContentPane(panel);
frame.setSize(512, 512);
frame.setVisible(true);
}
public static void main(String[] args){
EventQueue.invokeLater( ()->new RotateArrow().startGui() );
}
}

Related

Texture/background Image for polygon

I'm writing a tiled game board with hexagonal shape tiles using java swing. I'm able to draw polygon with help of this SOF thread.
Now I want to add background image to these hexagons and I have totally no idea how to do this. Here is a tutorial that draws background on "Rectangle" but how can I do the same on Hexagon?
Create the hexagon using a Shape. Probably a Polygon for this.
Set the Shape as a clip for the Graphics2D object.
Paint the image.
Move the Shape to the next location using an AffineTransform, a translate instance.
Rinse and repeat.
Like this:
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
class TexturedShape {
public static BufferedImage getTexturedImage(
BufferedImage src, Shape shp, int x, int y) {
Rectangle r = shp.getBounds();
// create a transparent image with 1 px padding.
BufferedImage tmp = new BufferedImage(
r.width+2,r.height+2,BufferedImage.TYPE_INT_ARGB);
// get the graphics object
Graphics2D g = tmp.createGraphics();
// set some nice rendering hints
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(
RenderingHints.KEY_COLOR_RENDERING,
RenderingHints.VALUE_COLOR_RENDER_QUALITY);
// create a transform to center the shape in the image
AffineTransform centerTransform = AffineTransform.
getTranslateInstance(-r.x+1, -r.y+1);
// set the transform to the graphics object
g.setTransform(centerTransform);
// set the shape as the clip
g.setClip(shp);
// draw the image
g.drawImage(src, x, y, null);
// clear the clip
g.setClip(null);
// draw the shape as an outline
g.setColor(Color.RED);
g.setStroke(new BasicStroke(1f));
g.draw(shp);
// dispose of any graphics object we explicitly create
g.dispose();
return tmp;
}
public static Shape getPointedShape(int points, int radius) {
double angle = Math.PI * 2 / points;
GeneralPath p = new GeneralPath();
for (int ii = 0; ii < points; ii++) {
double a = angle * ii;
double x = (Math.cos(a) * radius) + radius;
double y = (Math.sin(a) * radius) + radius;
if (ii == 0) {
p.moveTo(x, y);
} else {
p.lineTo(x, y);
}
}
p.closePath();
return p;
}
public static void main(String[] args) throws Exception {
URL url = new URL("http://i.stack.imgur.com/7bI1Y.jpg");
BufferedImage bi = ImageIO.read(url);
Shape hxgn = getPointedShape(6, 32);
final BufferedImage txtr = getTexturedImage(bi, hxgn, -200, -120);
Runnable r = new Runnable() {
#Override
public void run() {
JOptionPane.showMessageDialog(null,
new JLabel(new ImageIcon(txtr)));
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency
SwingUtilities.invokeLater(r);
}
}

How to draw part of a large BufferedImage?

I have a 10000x10000 BufferedImage and I'm looking to draw only part of it to a Canvas, is there a way to do this using args such as:
x, y, width, height ?
So for example, drawImage(img, x, y, width, height) would draw a rectangle from the image starting at (x, y) and having (width, height) as the dimensions?
EDIT:
I'm going to re- word this question:
I have a 10000x10000 image and I only want to display a portion of it on the screen, the problem with just offsetting it by x and y is that this still causes lag as the entire image is being rendered, just most of it off canvas. How can I basically make it so that the entire image is rendered but I can scroll around it without causing the canvas to lag?
I have a 10000x10000 BufferedImage and I'm looking to draw only part
of it to a Canvas, is there a way to do this using args such as:
Don't use canvas for custom painting in java. use JComponent or JPanel instead. It has a nice function paintComponent(Graphics g), override it and paint your image inside with g.drawImage(x, y, width, height, observer);
Swing graphics has Graphics.clipRect(int x, int y, int width, int height) to bound the area rectangle to which you wish to draw prior to drawing the image.
Edit (In response to your edited question):
First approach is to use BufferedImage..getSubimage(x, y, width, height) to get a sub image with specified rectangle region. It is faster.
BufferedImage img = ImageIO.read(new File("file"));
img = img.getSubimage(50, 50, 500, 500); // 500 x 500
This function will give you a new image cropped with the rectangle(x, y, width, height) of your original image you specified. Use the returned image to draw on your component.
Tutorial resource: Clipping the Drawing Region
Demo: Demonstrating clipping Image with Animation:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.*;
import java.util.logging.*;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.Timer;
class MyCanvas extends JPanel implements ActionListener
{
public BufferedImage buffImg;
public Rectangle rectangle;
Random random;
long lastTimeChanged;
int dirX = 1, dirY = 1;
volatile static boolean imageLoading = true;
public MyCanvas() {
random = new Random();
rectangle = new Rectangle(50, 50, 250, 250);
lastTimeChanged = System.currentTimeMillis();
setBackground(Color.WHITE);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if(imageLoading)
{
showWaitForLoading(g);
return;
}
g.clipRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
g.drawImage(buffImg, 0, 0, getWidth(), getHeight(), this);
}
public void showWaitForLoading(Graphics g)
{
Graphics2D g2d = (Graphics2D)g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.DARK_GRAY);
g2d.fillRoundRect(getWidth()/2-100, getHeight()/2-15, 200, 30, 30, 30);
g2d.setColor(Color.WHITE);
g2d.drawString("Loading image...", getWidth()/2 - 45, getHeight()/2 + 3 );
g2d.dispose();
}
#Override
public void actionPerformed(ActionEvent e) {
long endTime = System.currentTimeMillis();
if(endTime - lastTimeChanged > 500)
{
dirX = random.nextInt(2) == 0 ? -1 : 1;
dirY = random.nextInt(2) == 0 ? -1 : 1;
lastTimeChanged = endTime;
}
if(rectangle.x < 0)dirX = 1;
else if(rectangle.x + rectangle.width > getWidth())dirX = -1;
if(rectangle.y < 0)dirY = 1;
else if(rectangle.y + rectangle.height > getHeight())dirY = -1;
rectangle.x = rectangle.x + dirX * 10;
rectangle.y = rectangle.y + dirY * 10;;
repaint();
}
}
public class CustomPainting {
public static void main(String[] args) throws IOException {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
final MyCanvas canvas = new MyCanvas();
JFrame frame = new JFrame();
frame.setSize(new Dimension(500, 500));
frame.add(canvas);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Timer timer = new Timer(200, canvas);
timer.start();
new Thread()
{
public void run()
{
try {
canvas.buffImg = ImageIO.read(new URL("http://images6.fanpop.com/image/photos/33400000/Cute-Panda-beautiful-pictures-33434826-500-500.jpg"));
MyCanvas.imageLoading = false;
} catch (IOException ex) {
Logger.getLogger(CustomPainting.class.getName()).log(Level.SEVERE, null, ex);
}
}
}.start();
}
});
}
}
You can scale or draw a part of an image using Graphics.drawImage as mentioned another answer and according to Java documentation, ImageObserver argument is not needed for BufferedImage so you can just pass null.
http://docs.oracle.com/javase/7/docs/api/java/awt/Graphics.html
However, my choice would be the clipping drawing region of image instead.
Here is an example you can try:
Graphics2D g = BufferedImage.getGraphics;
g.setClip(x, y, width, height);
g.drawImage(sx, sy, x - sx, y - sy, null );
Yes there is: drawImage(Image img, int x, int y, int width, int height, ImageObserver observer)

Finding if my mouse is inside a rectangle in Java

I'm starting to develop a game and I need to be able to see if my mouse is inside a rectangle,
I've tried using .contains for a rectangle but i can't seem to get it to work, i'll paste my code below, any help would be greatly appreciated! :)
public boolean isMouseOver(GameContainer gc){
r = new Rectangle(getX(), getY(),getWidth(),getHeight());
Input input = gc.getInput();
xpos = input.getMouseX();
ypos = input.getMouseY();
return r.contains(xpos, ypos);
}
This is the method i'm trying to use, but it keeps returning false when the mouse is inside the rectangle. obviously, I initiated xpos, ypos, and the rectangle further up and I called the method in the update method of the class i'm trying to use it in.
You have two points for your mouse, it's x and y pos.
int mouseX = gc.getInput().getMouseX();
int mouseY = gc.getInput().getMouseY();
And we have a rectangle
Rectangle rec = new Rectangle( 100, 100, 200, 200 );
So we can check
if ( mouseX >= rec.getMinX() && mouseX <= rec.getMaxX ) // check if X is within range
&& ( mouseY >= rec.getMinY() && mouseY <= rec.getMaxY) // check if y is within range
Or now that we know our X value has to be greater than the rectangles low value but less than it's high value, and the same for Y lets check the contains function
contains(float xp, float yp, float xr, float yr, float widthr, float heightr)
xp - The x coordinate of the point to check
yp - The y coordinate of the point to check
xr - The x coordinate of the rectangle
yr - The y coordinate of the rectangle
widthr - The width of the rectangle
heightr - The height of the rectangle
So I'd say
contains( mouseX, mouseY, rec.getMinX(), rect.getMinY(), rec.getWidth(), rec.getHeight() )
Perhaps something was going wrong here?
Did you display the bounds of the Rectangle and the mouse position?
I would guess the Rectangle is relative to your component and the mouse is relative to the screen.
You can use the SwingUtilities class to do point conversions to make sure the points are relative to the same component.
Try out somethings like this..
Just run this below program, I hope you will get the answer:
package mouse;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.HeadlessException;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
public class Mouse extends JFrame implements MouseMotionListener {
private Image dbImage;
private Graphics dbg;
int mx, my;
boolean mouseDragged;
public Mouse() throws HeadlessException {
setSize(400, 300);
setResizable(false);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
addMouseMotionListener(this);
}
public void paint(Graphics g) {
dbImage = createImage(getWidth(), getHeight());
dbg = dbImage.getGraphics();
paintComponent(dbg);
g.drawImage(dbImage, 0, 0, this);
}
public void paintComponent(Graphics g) {
if (mouseDragged){
g.setColor(Color.DARK_GRAY);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.LIGHT_GRAY);
g.fillRect(mx, my, 20, 20);
}else{
g.setColor(Color.LIGHT_GRAY);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.DARK_GRAY);
g.fillRect(mx, my, 20, 20);
}
repaint();
}
public static void main(String[] args) {
Mouse mouse = new Mouse();
}
#Override
public void mouseDragged(MouseEvent e) {
mx = e.getX() - 10;
my = e.getY() - 10;
mouseDragged = true;
e.consume();
}
#Override
public void mouseMoved(MouseEvent e) {
mx = e.getX();
my = e.getY();
mouseDragged = false;
e.consume();
}
}
Also there are lot of videos on you tube. I will post the link as well. Check this youtube channel : http://www.youtube.com/watch?v=PopdTUzizDA

Drawing a nice circle in Java

I'm using Java Graphics and I keep getting "ugly" circles.
Here's what my Java program makes
And here's the same thing being made in Matlab
I think it is clear that the Java one is not as "nice" looking as the Matlab one, particularly on the edges of the circle. Note that this has nothing to do with the resolution...these images are practically the same size. Also note that I am already setting rendering hints.
Here's a stand alone with a Main function you can run to test this out.
package test;
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.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
public class SimplePaint02 {
private static final int LINE_THICKNESS = 4;
private static final int LINE_GAP = 10;
private Color lineColor = Color.red;
public static void main(String[] args) {
new SimplePaint02();
}
public SimplePaint02() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame();
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(100, 100);
}
#Override
public void paintComponent(Graphics g) {
int radius = 50;
BufferedImage buffer = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = buffer.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR);
Ellipse2D circle = new Ellipse2D.Float(0, 0, radius,radius);
Shape clip = g2d.getClip();
g2d.setClip(circle);
AffineTransform at = g2d.getTransform();
g2d.setTransform(AffineTransform.getRotateInstance(Math.toRadians(45),radius / 2, radius / 2));
int gap = LINE_GAP;
g2d.setColor(Color.WHITE);
g2d.fill(circle);
g2d.setColor(lineColor);
//g2d.setStroke(new BasicStroke(LINE_THICKNESS));
for (int index = 0; index < 10; index++) {
int x1 = index*gap-(LINE_THICKNESS/2);
int y1 = 0;
int x2 = index*gap+(LINE_THICKNESS/2);
int y2 = radius;
int width = x2 - x1;
int height = y2 - y1;
g2d.fillRect(x1, y1, width, height);
//g2d.drawLine(index * gap, 0, index * gap, getRadius());
}
g2d.setTransform(at);
g2d.setClip(clip);
g2d.dispose();
g.drawImage(buffer, 0, 0, this);
}
}
}
EDIT: Please see Code Guy's answer below for a solution. This is marked correct because it was Joey Rohan who figured it out initially!
I got smooth edge when i tried out same thing:
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
public class DrawSmoothCircle {
public static void main(String[] argv) throws Exception {
BufferedImage bufferedImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bufferedImage.createGraphics();
g2d.setRenderingHint (RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint(Color.green);
g2d.fillOval(10, 10, 50, 50);
g2d.dispose();
ImageIO.write(bufferedImage, "png", new File("e:\\newimage.png"));
}
}
UPDATE:
After searching alot:
There is nothing wrong with the code but,
Well, unfortunately Java 2D (or at least Sun's current implementation) does not support "soft clipping."
But Also got a trick for the clips:
Follow This link,you can achieve what you are asking for.
(Also, i got a smooth edge, cause i din't use clip stuff,in my above image)
Here was the answer. I adapted the majority of the code from this site. Take a look:
Here's the code:
public void paintComponent(Graphics g) {
// Create a translucent intermediate image in which we can perform
// the soft clipping
GraphicsConfiguration gc = ((Graphics2D) g).getDeviceConfiguration();
BufferedImage img = gc.createCompatibleImage(getWidth(), getHeight(), Transparency.TRANSLUCENT);
Graphics2D g2 = img.createGraphics();
// Clear the image so all pixels have zero alpha
g2.setComposite(AlphaComposite.Clear);
g2.fillRect(0, 0, getWidth(), getHeight());
// Render our clip shape into the image. Note that we enable
// antialiasing to achieve the soft clipping effect. Try
// commenting out the line that enables antialiasing, and
// you will see that you end up with the usual hard clipping.
g2.setComposite(AlphaComposite.Src);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.WHITE);
g2.fillOval(0, 0, getRadius(), getRadius());
// Here's the trick... We use SrcAtop, which effectively uses the
// alpha value as a coverage value for each pixel stored in the
// destination. For the areas outside our clip shape, the destination
// alpha will be zero, so nothing is rendered in those areas. For
// the areas inside our clip shape, the destination alpha will be fully
// opaque, so the full color is rendered. At the edges, the original
// antialiasing is carried over to give us the desired soft clipping
// effect.
g2.setComposite(AlphaComposite.SrcAtop);
g2.setColor(lineColor);
int gap = LINE_GAP;
AffineTransform at = g2.getTransform();
g2.setTransform(AffineTransform.getRotateInstance(Math.toRadians(45),getRadius() / 2, getRadius() / 2));
for (int index = 0; index < 10; index++) {
int x1 = index*gap-(LINE_THICKNESS/2);
int y1 = 0;
int x2 = index*gap+(LINE_THICKNESS/2);
int y2 = getRadius();
int width = x2 - x1;
int height = y2 - y1;
g2.fillRect(x1, y1, width, height);
}
g2.setTransform(at);
g2.dispose();
// Copy our intermediate image to the screen
g.drawImage(img, 0, 0, null);
}
Update
OK. Then the idea is to not use clipping at all but instead to make the clipped shape by subtracting areas from each other.
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class SimplePaint02 {
private static final int LINE_THICKNESS = 4;
private static final int LINE_GAP = 10;
private Color lineColor = Color.red;
public static void main(String[] args) {
new SimplePaint02();
}
public SimplePaint02() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame();
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 {
int radius = 75;
#Override
public Dimension getPreferredSize() {
return new Dimension(radius, radius);
}
#Override
public void paintComponent(Graphics g) {
Ellipse2D circle = new Ellipse2D.Float(0, 0, radius, radius);
Area lines = new Area();
int gap = LINE_GAP;
for (int index = 0; index < 10; index++) {
int x1 = index * gap - (LINE_THICKNESS / 2);
int y1 = 0;
int x2 = index * gap + (LINE_THICKNESS / 2);
int y2 = radius;
int width = x2 - x1;
int height = y2 - y1;
Shape lineShape = new Rectangle2D.Double(x1, y1, width, height);
lines.add(new Area(lineShape));
//g3d.fillRect(x1, y1, width, height);
//g2d.drawLine(index * gap, 0, index * gap, getRadius());
}
//g2d.setClip(circle);
Area circleNoLines = new Area(circle);
circleNoLines.subtract(lines);
Area linesCutToCircle = new Area(circle);
linesCutToCircle.subtract(circleNoLines);
//g2d.setTransform(at);
BufferedImage buffer = new BufferedImage(radius * 2, radius * 2, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = buffer.createGraphics();
RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setTransform(AffineTransform.getRotateInstance(Math.toRadians(45), radius / 2, radius / 2));
g2d.setRenderingHints(rh);
g2d.setColor(Color.ORANGE);
g2d.fill(linesCutToCircle);
g2d.setColor(Color.RED);
g2d.fill(circleNoLines);
g2d.dispose();
g.drawImage(buffer, 0, 0, this);
}
}
}
Old code
Part of the problem is that the rendering operations typically do not apply to a Clip, though they will apply to the Shape when it is drawn. I generally solve that by (last) painting the Shape itself. E.G.
A 1.5 pixel BasicStroke is used here for the red circle - smoothing the rough edges produced by the Clip.
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
public class SimplePaint02 {
private static final int LINE_THICKNESS = 4;
private static final int LINE_GAP = 10;
private Color lineColor = Color.red;
public static void main(String[] args) {
new SimplePaint02();
}
public SimplePaint02() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame();
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 {
int radius = 75;
#Override
public Dimension getPreferredSize() {
return new Dimension((int)(1.1*radius), (int)(1.1*radius));
}
#Override
public void paintComponent(Graphics g) {
BufferedImage buffer = new BufferedImage(radius*2, radius*2, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = buffer.createGraphics();
RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
rh.put(RenderingHints.KEY_DITHERING,RenderingHints.VALUE_DITHER_ENABLE);
rh.put(RenderingHints.KEY_COLOR_RENDERING,RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHints(rh);
Ellipse2D circle = new Ellipse2D.Float(0, 0, radius,radius);
Shape clip = g2d.getClip();
g2d.setClip(circle);
AffineTransform at = g2d.getTransform();
g2d.setTransform(AffineTransform.getRotateInstance(Math.toRadians(45),radius / 2, radius / 2));
int gap = LINE_GAP;
g2d.setColor(Color.WHITE);
g2d.fill(circle);
g2d.setColor(lineColor);
//g2d.setStroke(new BasicStroke(LINE_THICKNESS));
for (int index = 0; index < 10; index++) {
int x1 = index*gap-(LINE_THICKNESS/2);
int y1 = 0;
int x2 = index*gap+(LINE_THICKNESS/2);
int y2 = radius;
int width = x2 - x1;
int height = y2 - y1;
g2d.fillRect(x1, y1, width, height);
//g2d.drawLine(index * gap, 0, index * gap, getRadius());
}
g2d.setTransform(at);
g2d.setClip(clip);
g2d.setClip(null);
g2d.setStroke(new BasicStroke(1.5f));
g2d.draw(circle);
g2d.dispose();
g.drawImage(buffer, 0, 0, this);
}
}
}
I used drawPolygon method to draw circle by generating array of most of the points on circumference of circle with proposed radius.
Code:
import java.awt.*;
import java.applet.*;
/*<applet code="OnlyCircle" width=500 height=500>
</applet>*/
public class OnlyCircle extends Applet{
public void paint(Graphics g){
int r=200;//radius
int x1=250;//center x coordinate
int y1=250;//center y coordinate
double x2,y2;
double a=0;
double pi=3.14159;
int count=0;
int i=0;
int f=0;
int[] x22=new int[628319];
int[] y22=new int[628319];
while(a<=2*pi&&i<628319&&f<628319)
{
double k=Math.cos(a);
double l=Math.sin(a);
x2=x1+r*k;
y2=y1+r*l;
x22[i]=(int)x2;
y22[f]=(int)y2;
i++;
f++;
a+=0.00001;
}
int length=x22.length;
g.drawPolygon(x22,y22,length);
}
}
You can enable anti-aliasing:
Graphics2D g2 = (Graphics2D) g;
Map<RenderingHints.Key, Object> hints = new HashMap<RenderingHints.Key, Object>();
hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHints(hints);
I also suggest you draw to the Graphics object you get from the paintComponent method rather than creating an intermediate BufferedImage.

Triangle inside rectangular grids

I have written a code that draws grids and a triangle inside one grid cell. The grid size is increased/decreased when the window is maximized or minimized.
My requirement is that the triangle size should also increase/decrease to fit the grid cell each time the grid size is increased/decreased.
My code is as follows:
import java.awt.BasicStroke;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Grid extends JPanel {
/**
* #param args
*/
public static void main(String[] args) {
Grid g = new Grid();
JFrame f = new JFrame("Application GUI Window");
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {System.exit(0);}
});
f.getContentPane().add("Center", g);
f.pack();
f.setSize(new Dimension(450,400));
f.show();
}
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
Dimension d = getSize();
g2d.setBackground(getBackground());
g2d.clearRect(0, 0, d.width, d.height);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
drawGrid(d.width, d.height, g2d);
int[] xPoints = {20,15,25};
int[] yPoints = {15,25,25};
int n = 3;
Polygon triangle = new Polygon(xPoints, yPoints, n);
g.fillPolygon(triangle);
}
private void drawGrid(int width, int height, Graphics2D g2d) {
/* BasicStroke border = new BasicStroke(3, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND, 0, new float[]{0,1,0,1}, 0);
g2d.setStroke(border);
g2d.drawRect(3,3,width-6,height-6);*/
//horizontal lines
int cellheight = height/10;
int cellwidth = width/5;
for (int j=0;j<height;j=j+cellheight)
{
BasicStroke line = new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND, 0, new float[]{0,1,0,1}, 0);
g2d.setStroke(line);
g2d.drawLine(0, j, cellwidth*5, j);
}
//vertical lines
for (int i=0;i<width;i=i+cellwidth)
{
BasicStroke line = new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND, 0, new float[]{0,1,0,1}, 0);
g2d.setStroke(line);
g2d.drawLine(i, 0, i, cellheight*10);
}
}
}
Thanks in advance for your help.
You can tie the coordinates of the triangle to the dimensions of a grid cell. Here is a snippet based on your example:
int cellHeight = d.height/10;
int cellWidth = d.width/5;
int xOffset = cellWidth/6;
int yOffset = cellHeight/6;
int[] xPoints = {cellWidth/2, xOffset, cellWidth - xOffset};
int[] yPoints = {yOffset, cellHeight - yOffset, cellHeight - yOffset};
Polygon triangle = new Polygon(xPoints, yPoints, xPoints.length);
Note that in Swing you usually should override paintComponent() rather than paint(), unless in some special cases. See A Closer Look at the Paint Mechanism for more details.
Also note that show() is deprecated in favor of setVisible(). You can replace it with: setVisible(true)

Categories