Java: Drawing using Graphics outside the class which draws the main canvas - java

I'm trying to develop a Tic Tac Toe game in Java using Graphics.
My problem is: I don't want to add any other methods inside my Grid.class (which draws the lines 3x3), but i want to draw my X or O from a class called Game. My grid class looks like the following:
import java.awt.Graphics;
import javax.swing.JPanel;
public class Grid extends JPanel{
private final int ITEM_WIDTH = 30;
private final int ITEM_HEIGHT = 30;
private final int OUTER_WIDTH = 90;
private final int OUTER_HEIGHT = 90;
public void paintComponent(Graphics g){
super.paintComponent(g);
drawOuter(g);
drawGrid(g);
}
public void drawOuter(Graphics g){
g.drawRect(0, 0, OUTER_WIDTH, OUTER_HEIGHT);
}
public void drawGrid(Graphics g){
//Vertikális
for(int i = ITEM_WIDTH; i < OUTER_WIDTH; i += ITEM_WIDTH){
g.drawLine(i, 0, i, OUTER_HEIGHT);
}
//Horizontális
for(int i = ITEM_HEIGHT; i < OUTER_HEIGHT; i += ITEM_HEIGHT){
g.drawLine(0, i, OUTER_WIDTH, i);
}
}
Thank you for your helps

My suggestion would be try to keep things as simple as possible.
Create ImageIcons that hold your X and O images
Have a GridLayout of JLabels placed in your JPanel above.
Swapping the JLabel's ImageIcon when I wanted to display an X, an O, or a blank.
Proof of concept code. Clicking on the JLabels several times will show the icons. The code has no Tic-Tac-Toe logic though:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class XorOorBlank extends JPanel {
private static final int IMG_WIDTH = 100;
private static final Color BACKGROUND = Color.LIGHT_GRAY;
private static final Color X_COLOR = Color.red;
private static final Color O_COLOR = Color.blue;
private static final Stroke X_STROKE = new BasicStroke(8f);
private static final int GAP = 8;
private static final int SIDE = 3;
private Icon blankIcon = createBlankIcon();
private Icon xIcon = createXIcon();
private Icon oIcon = createOIcon();
public XorOorBlank() {
setBackground(BACKGROUND);
setLayout(new GridLayout(SIDE, SIDE, GAP, GAP));
MouseListener mouseListener = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
JLabel label = (JLabel) e.getSource();
Icon icon = label.getIcon();
if (icon == blankIcon) {
icon = xIcon;
} else if (icon == xIcon) {
icon = oIcon;
} else if (icon == oIcon) {
icon = blankIcon;
}
label.setIcon(icon);
}
};
for (int i = 0; i < SIDE; i++) {
for (int j = 0; j < SIDE; j++) {
JLabel label = new JLabel(blankIcon);
label.addMouseListener(mouseListener);
add(label);
}
}
}
private Icon createBlankIcon() {
BufferedImage img = new BufferedImage(IMG_WIDTH, IMG_WIDTH,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setColor(Color.white);
g2.fillRect(0, 0, IMG_WIDTH, IMG_WIDTH);
g2.dispose();
ImageIcon icon = new ImageIcon(img);
return icon;
}
private Icon createXIcon() {
BufferedImage img = new BufferedImage(IMG_WIDTH, IMG_WIDTH,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setColor(Color.white);
g2.fillRect(0, 0, IMG_WIDTH, IMG_WIDTH);
g2.setColor(X_COLOR);
g2.setStroke(X_STROKE);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int x1 = GAP;
int y1 = x1;
int x2 = IMG_WIDTH - GAP;
int y2 = x2;
g2.drawLine(x1, y1, x2, y2);
g2.drawLine(x2, y1, x1, y2);
g2.dispose();
ImageIcon icon = new ImageIcon(img);
return icon;
}
private Icon createOIcon() {
BufferedImage img = new BufferedImage(IMG_WIDTH, IMG_WIDTH,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setColor(Color.white);
g2.fillRect(0, 0, IMG_WIDTH, IMG_WIDTH);
g2.setColor(O_COLOR);
g2.setStroke(X_STROKE);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int x1 = GAP;
int y1 = x1;
int x2 = IMG_WIDTH - 2 * GAP;
int y2 = x2;
g2.drawOval(x1, y1, x2, y2);
g2.dispose();
ImageIcon icon = new ImageIcon(img);
return icon;
}
private static void createAndShowGui() {
JFrame frame = new JFrame("X or O");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new XorOorBlank());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Note: not a paintComponent method to be found.
This displays:

You have a few choices...
You Could
Extend from Grid, overriding it's paintComponent method and draw the X/Os within it (calling super.paintComponent)
You Could
Create a interface which provide a simple "draw" method, which when implemented, would paint either X/O. You could then simply add an new instance of this to the Grid class and have it paint the X/O via some kind of loop within the paintComponent method
You Could
Use, something like GridBagLayout or even OverlayLayout and place another panel over the top of the Grid class, or, using BorderLayout, add the panel directly to the Grid class, which would then be responsible for painting the X/O
You Could
Do what Hovercraft Full Of Eels has suggested...

Related

Generate square wave in JFrame

I have inefficient code of a square wave. I have 2 buttons, 1 table and something like a coordinate system where the square appears in. I want the wave to scroll/move in real time until it hits the end of the coordinate system instead of just appearing by selecting both of the buttons. Additionally, if anyone has a better way of drawing a square wave please tell me.
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawLine(20, 300, 20, 450);
g2d.drawLine(20, 350, 400, 350);
g2d.drawLine(20, 400, 400, 400);
g2d.drawLine(20, 450, 400, 450);
if (this.jButtonSTART.isSelected() & this.jButtonAND.isSelected()) {
this.draw(g2d);
}
}
public void draw(Graphics2D g2d) {
boolean up = true;
while (x <= 380) {
g2d.setColor(Color.blue);
if (x > 0 && x % 95 == 0) {
up = !up;
g2d.drawLine(20 + x, up ? 315 : 350 + y, 20 + x, up ? 350 : 315 + y);
} else {
if (up) {
g2d.drawLine(20 + x, 315 + y, 21 + x, y + 315);
} else {
g2d.drawLine(20 + x, 350 + y, 21 + x, y + 350);
}
}
x++;
}
x = 0;
}
Simple way to draw your square wave and move it:
Create a BufferedImage that is longer than your GUI. It should have length that matches a the period of your square wave and be at least twice as long as the GUI component that it's displayed in.
Draw within the paintComponent method override of a JPanel, not the paint method.
Call the super's paintComponent method first within your override.
You'll draw the image using g.drawImage(myImage, imageX, imageY, this) where imageX and imageY are private instance fields of the JPanel-extending drawing class.
In a Swing Timer, advance imageX with each tick of the Timer, that is each time its ActionListener's actionPerformed method is called).
Then call repaint() on the drawing JPanel within the same actionPerformed method.
Done.
for example, note that this code does not do exactly what you're trying to do, but does show an example of Swing animation using a Swing Timer and paintComponent.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
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.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class MoveWave extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = 200;
private static final int TIMER_DELAY = 40;
public static final int DELTA_X = 2;
public static final int STARTING_MY_IMAGE_X = -PREF_W;
private static final Color COLOR_1 = Color.RED;
private static final Color COLOR_2 = Color.BLUE;
private static final Color BG = Color.BLACK;
private static final int CIRCLE_COUNT = 10;
private BufferedImage myImage = null;
private int myImageX = STARTING_MY_IMAGE_X;
private int myImageY = 0;
public MoveWave() {
setBackground(BG);
myImage = new BufferedImage(2 * PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = myImage.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(new GradientPaint(0, 0, COLOR_1, 20, 20, COLOR_2, true));
for (int i = 0; i < CIRCLE_COUNT; i++) {
int x = (i * 2 * PREF_W) / CIRCLE_COUNT;
int y = PREF_H / 4;
int width = (2 * PREF_W) / CIRCLE_COUNT;
int height = PREF_H / 2;
g2.fillOval(x, y, width, height);
}
g2.dispose();
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (myImage != null) {
g.drawImage(myImage, myImageX, myImageY, this);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
myImageX += DELTA_X;
if (myImageX >= 0) {
myImageX = STARTING_MY_IMAGE_X;
}
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("MoveWave");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new MoveWave());
frame.setResizable(false);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}

VPL start with java

I am trying to create a generic node that can modified and to ones wishes but I have run in some trouble because my skills in Swing and such is not so good.
Problem 1: I can't seem to get two Node/Jpanels in the same JFrame.
lesser Problem 2: The stroke is cut off from the JPanel border (Should I build in a kind of bleed so this will not happen?)
Any aid is helpful :)
public class SimpleGui {
public SimpleGui() {
JFrame frame = new JFrame();
NodePanel panel = new NodePanel(3, 2);
panel.setLayout(new javax.swing.SpringLayout());
frame.add(panel);
NodePanel panel2 = new NodePanel(3, 2);
panel2.setLayout(new javax.swing.SpringLayout());
frame.add(panel2);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 300);
frame.setResizable(true);
frame.setVisible(true);
panel.setLocation(50, 50);
panel2.setLocation(150, 150);
}
}
The generic node/panel class that is added to the JFrame:
public class NodePanel extends JPanel {
private int x = 0, int y = 0;
private PortPanel inPortPanel;
private PortPanel outPortPanel;
int iH;
int oH;
private int width = 120;
private int height = 30;
Graphics2D g2;
public NodePanel(int input, int output) {
inPortPanel = new PortPanel(input);
this.add(inPortPanel);
outPortPanel = new PortPanel(output);
this.add(outPortPanel);
setPreferredSize(calculateDimension());
}
public void paintComponent(Graphics g) {
g2 = (Graphics2D) g;
g2.setColor(Color.black);
g2.setStroke(new BasicStroke(4));
inPortPanel.setLocation(x, y + (height - iH) / 2);
outPortPanel.setLocation(x + width, y + (height - oH) / 2);
drawNode();
}
private void drawNode() {
g2.drawRoundRect(x, y, width, height, 5, 5);
}
private Dimension calculateDimension() {
iH = inPortPanel.getPreferredSize().height;
int iW = inPortPanel.getPreferredSize().width;
oH = outPortPanel.getPreferredSize().height;
height = iH > oH ? iH + iW : oH + iW;
return new Dimension(width, height);
}
}
The port panel that is part of the Node Panel
public class PortPanel extends JPanel {
int x = 0;
int y = 0;
int portWidth = 14;
int portHeight = 14;
int count = 0;
int offset = 22;
Graphics2D g2;
public PortPanel(int i) {
this.count = i;
setPreferredSize(calculateDimensions());
}
public void paintComponent(Graphics g) {
g2 = (Graphics2D) g;
g2.setColor(Color.black);
drawPorts();
}
private void drawPorts() {
for (int i = 0; i < count; i++) {
g2.fillOval(x, y + (i * offset), portWidth, portHeight);
}
}
private Dimension calculateDimensions(){
int overallHeight = (offset * (count-1)) + portHeight;
return new Dimension(portWidth,overallHeight);
}
}
Node should be a logical class, not a GUI component class.
You should have a single drawing JPanel that can draw all the visual representation of your logical entities.
This way the model can hold multiple logical entities that all can be drawn by the single drawing JPanel without having to worry as much about layout managers.
Don't forget to call your JPanel's super.paintComponent method within its override method so your JPanel can do house-keeping painting.
Avoid giving your JPanels Graphics or Graphics2D fields as that increases the risk of your code throwing a NPE. Use the Graphics object given to your paintComponent method, and if you need to use it in another method that is called by paintComponent, pass it into that method.

Drawing lines on top of image without using AlphaComposite

I am currently having lots of difficulty in one of my assignments. The task for this assignment is to create an aviation radar, VOR, with java's GUI features. When the user presses either the left/right arrow on their keyboard, the circular radar should rotate, and a needle in the middle of the radar should move left/right.
I am very close to finishing, but I am stuck on one part. I drew an image of the radar, and I attempted to draw a line on top of it. However, I was only successful when I was able to make all JPanels transparent. This causes a problem because it is difficult to see what I drew on top of it.
So, my question is, how would I draw it on top of the radar image without making everything transparent? Below is my code
public class finalVORGUI extends JPanel{
private JPanel rotationPanel;
private JPanel needle;
private JPanel attributes;
private int degrees;
private String CurrentRadial;
private int x;
private int y1;
private int y2;
final int WIDTH = 600;
final int HEIGHT = 600;
private ImageIcon radar = new ImageIcon("image/vor1.png");
/**
* The constructor for the class
* It's going to set the dimension of the program to 600x600, the
* background is going to be white (in order to blend in with the
* vor image), and it is going to add in the VOR radar and a radial
* indicator that will let the user know which radial he/she is on
*/
public finalVORGUI(){
JLayeredPane lp = new JLayeredPane();
lp.setPreferredSize(new Dimension(WIDTH, HEIGHT));
setBackground(Color.white);
lp.setLayout(null);
lp.setFocusable(true);
lp.addKeyListener(new KeyboardListener());
rotationPanel = new JPanel();
rotationPanel = new TurningCanvas();
needle = new JPanel();
needle = new DrawNeedle();
attributes = new JPanel();
attributes = new DrawAttributes();
lp.add(rotationPanel, Integer.valueOf(1));
lp.add(needle, Integer.valueOf(2));
lp.add(attributes, Integer.valueOf(3));
needle.setBounds(100,0, needle.getPreferredSize().width, needle.getPreferredSize().height);
rotationPanel.setBounds(100, 100, rotationPanel.getPreferredSize().width, rotationPanel.getPreferredSize().height);
attributes.setBounds(100, 100, rotationPanel.getPreferredSize().width, rotationPanel.getPreferredSize().height);
add(lp);
degrees = 360; //to edit: this is going to be the radial the radar is currently facing
x = 172; //x is the location of the needle
y1 = 155;
y2 = 330;
CurrentRadial = "Radial: " + degrees; //A string that is always going to be above the radar. it's going to let the user know the current radial
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawString(CurrentRadial, 250, 100);
}
public class DrawAttributes extends JPanel{
public DrawAttributes(){
setOpaque(false);
add(new Attributes());
}
public class Attributes extends JPanel{
int w = 500;
int h = 400;
public Attributes(){
setPreferredSize(new Dimension(w,h));
setBackground(Color.white);
}
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
g2.drawString("To",300,400);
g2.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, 0.3f));
g2.setStroke(new BasicStroke(3));
super.paintComponent (g);
g2.dispose();
}
}
}
public class DrawNeedle extends JPanel{ //todo: make sure this works and adds correctly to the LayeredPane
public DrawNeedle(){
setOpaque(false);
add(new Needle());
}
public class Needle extends JPanel{
int w = 500;
int h = 400;
public Needle(){
setPreferredSize(new Dimension(w,h));
setBackground(Color.white);
}
private void doDrawing(Graphics g){
Graphics2D g4 = (Graphics2D) g;
g4.drawString("TO", 190, 200);
g4.drawString("FROM",190, 300);
}
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
RenderingHints hints = new RenderingHints(null);
hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
hints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2.setRenderingHints(hints);
g2.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, 0.3f));
g2.setStroke(new BasicStroke(3));
doDrawing(g);
g2.drawLine(x,y1,x,y2);
super.paintComponent (g);
g2.dispose();
}
}
}
public class TurningCanvas extends JPanel{
public TurningCanvas(){
setOpaque(false);
add(new TurningImage());
}
public class TurningImage extends JPanel{
int w = radar.getIconWidth()- 20;
int h = radar.getIconHeight() -20;
public TurningImage(){
setPreferredSize(new Dimension(w,h));
setBackground(Color.white);
}
public void paintComponent(Graphics g){
super.paintComponent (g);
Graphics2D g2 = (Graphics2D) g;
RenderingHints hints = new RenderingHints(null);
hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
hints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2.setRenderingHints(hints);
g2.rotate (Math.toRadians(degrees),w/2, h/2);
g2.drawImage(radar.getImage(), 0, 0, this);
//g2.drawLine(171,0,x,300);
g2.dispose();
}
}
}
/**
* This is the keyboard listener that this program will be using
* Depending on what the user wishes, or what type of keyboard they have,
* they will be able to find their desired radial by using the arrow keys
* or the "a" and "d" buttons. Left arrow to minus radial, right arrow to plus radial, etc etc...
*
*/
public class KeyboardListener implements KeyListener{
public void keyPressed (KeyEvent event) {
if(event.getKeyCode() == KeyEvent.VK_LEFT){
degrees--;
x--;
if(degrees <= 0){
degrees = 360;
}
if(x <= 89){
x = 89;
}
CurrentRadial = "Radial: " + degrees;
repaint();
}
if(event.getKeyCode() == KeyEvent.VK_RIGHT){
degrees++;
x++;
if(degrees >= 360){
degrees = 1;
}
if(x >= 250){
x = 250;
}
CurrentRadial = "Radial: " + degrees;
repaint();
}
}
public void keyTyped (KeyEvent event) {}
public void keyReleased (KeyEvent event) {}
}
/**
* The main method of this class
* This is going to make a new JFrame, which will hold the new
* VOR radar
* #param args
*/
public static void main(String[] args){
finalVORGUI test = new finalVORGUI();
JFrame frame = new JFrame("VOR Radar");
frame.setContentPane(test);
frame.pack();
frame.setVisible(true);
}
}

Image Pan and Zooming

I want to display an image to zoom in and out and can be shifted. I have the script below, but it can not be shifted. how to fix it.
the code :
package dispertasih;
import static dispertasih.PanPanel.startX;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;
public class PadAndZoom implements ChangeListener {
BufferedImage image;
PanPanel label;
public void stateChanged(ChangeEvent e) {
int value = ((JSlider)e.getSource()).getValue();
double scale = value/200.0;
BufferedImage scaled = getScaledImage(scale);
label = new PanPanel(scaled);
label.revalidate(); // signal scrollpane
}
private BufferedImage getScaledImage(double scale) {
int w = (int)(scale*image.getWidth());
int h = (int)(scale*image.getHeight());
BufferedImage bi = new BufferedImage(w, h, image.getType());
Graphics2D g2 = bi.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BICUBIC);
AffineTransform at = AffineTransform.getScaleInstance(scale, scale);
g2.drawRenderedImage(image, at);
g2.dispose();
return bi;
}
private PanPanel getContent() {
createAnImage();
label = new PanPanel(image);
//label.setHorizontalAlignment(JLabel.CENTER);
return label;
}
private void createAnImage() {
int w = 500;
int h = 500;
int type = BufferedImage.TYPE_INT_RGB; // many options
try {
image = ImageIO.read(PadAndZoom.class.getResource("/tampilan/background5.jpg"));
} catch (IOException ex) {
Logger.getLogger(PadAndZoom.class.getName()).log(Level.SEVERE, null, ex);
}
}
private JSlider getControl() {
JSlider slider = new JSlider(JSlider.HORIZONTAL, 50, 200, 50);
slider.setMajorTickSpacing(50);
slider.setMinorTickSpacing(10);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.addChangeListener(this);
return slider;
}
public static void main(String[] args) {
PadAndZoom app = new PadAndZoom();
JFrame f = new JFrame();
JButton b = new JButton();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new JScrollPane(app.getContent()));
//f.getContentPane().add(new JButton("tessss"));
f.getContentPane().add(app.getControl(), "Last");
f.setSize(400, 400);
f.setLocation(200,200);
f.setVisible(true);
}
}
class PanPanelX extends JPanel {
private int x, y;
private int width = 800, height = 800;
BufferedImage img;
private final static RenderingHints textRenderHints = new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
private final static RenderingHints imageRenderHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
private final static RenderingHints renderHints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
static int startX, startY;
public PanPanelX(BufferedImage img) {
x = 20;
y = 20;
this.img = img;
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent me) {
super.mousePressed(me);
startX = me.getX();
startY = me.getY();
}
});
addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent me) {
super.mouseDragged(me);
if (me.getX() < startX) {//moving image to right
x -= 2;
} else if (me.getX() > startX) {//moving image to left
x += 2;
}
if (me.getY() < startY) {//moving image up
y -= 2;
} else if (me.getY() > startY) {//moving image to down
y += 2;
}
repaint();
}
});
}
#Override
protected void paintComponent(Graphics grphcs) {
super.paintComponent(grphcs);
Graphics2D g2d = (Graphics2D) grphcs;
//turn on some nice effects
applyRenderHints(g2d);
g2d.drawImage(img, x, y, null);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
public static void applyRenderHints(Graphics2D g2d) {
g2d.setRenderingHints(textRenderHints);
g2d.setRenderingHints(imageRenderHints);
g2d.setRenderingHints(renderHints);
}
}
i think on :
public void stateChanged(ChangeEvent e) {
int value = ((JSlider)e.getSource()).getValue();
double scale = value/200.0;
BufferedImage scaled = getScaledImage(scale);
label = new PanPanel(scaled);
label.revalidate(); // signal scrollpane
}
there are mistakes, but I've stuck fixing it....
thank you ...
The problem is, the reference of the image that is displayed on the screen is not the same as the image you are creating when you resize it...
You create PanPanel by using...
BufferedImage scaled = getScaledImage(scale);
label = new PanPanel(scaled);
getScaledImage creates a new image each time it is called, based on the original image.
BufferedImage bi = new BufferedImage(w, h, image.getType());
This means, that the image in PadAndZoom is not the same as the one in PanPanel, so any changes you make to image or any new scaled instances will never be painted because PanPanel knows nothing about them...
Now, in stateChanged, instead of updating the instance of PanPanel that you already have, you create a new instance of it...and that's it...it's never added to any displayable component, so it will never appear on the screen.
Instead, in your PanPanel, you need to supply some way for the stateChanged method to pass the scaled instance of the image to it...
public void setImage(BufferedImage img) {
this.img = img;
revalidate();
repaint();
}
Then in your stateChanged method you can do...
int value = ((JSlider) e.getSource()).getValue();
double scale = value / 200.0;
BufferedImage scaled = getScaledImage(scale);
label.setImage(scaled);

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.

Categories