I'm drawing a knob as a component and I'm using swing. The result seems ok but when I click on the knob and move my mouse (up or down) to change the knob position, the knob repainted is ugly, as if there were 2 layers superposed. More over, I have this result only for the "small sizes" of my knob. If I enlarge my frame, this ugly effect disappear. Does someone could explain me what happens and how to improve my knob ?
Thanks a lot for those who will help me.
Here is my code :
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Line2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Knob extends JPanel implements MouseListener {
int length, originX, originY, centerX, centerY, width, height, diameter, squareLength;
int minorTick, majorTick, xTick, yTick, xCursor, yCursor;
int xMouse, yMouse, xMouseOrigin, yMouseOrigin;
float yDeltaMouse;
double angleOrigin, angleRange, angle;
double cursorValue;
double initialCursorValue;
Color backgroundColor, knobColor;
boolean mousePressed;
Thread t;
private double knobValue;
private String title = new String("");
private int titleWidth;
Knob (double initialValue, Color c, int majorTick, int minorTick) {
//System.out.println("Knob");
cursorValue=initialCursorValue=initialValue;
knobColor =c;
this.majorTick=majorTick;
this.minorTick=minorTick;
this.addMouseListener(this);
}
Knob (double initialValue, Color c, int majorTick, int minorTick, String title) {
//System.out.println("Knob");
cursorValue=initialCursorValue=initialValue;
knobColor =c;
this.majorTick=majorTick;
this.minorTick=minorTick;
this.title=title;
this.addMouseListener(this);
}
public void paintComponent (Graphics g) {
width=this.getWidth()/10*10;
height=this.getHeight()/10*10;
Graphics2D g2D = (Graphics2D)g;
System.setProperty("awt.useSystemAAFontSettings", "on");
System.setProperty("swing.aatext", "true");
g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if (width>height) {
length=height;
} else {
length=width;
}
centerX=width/2;
centerY=height/2;
g.setColor(Color.BLACK);
squareLength = (int )(length*0.9);
originX=(width-squareLength)/2;
originY=(height-squareLength)/2;
/*
//-45 = (-(Math.PI)/4)
angleOrigin= -45;
// 270 = (3*(Math.PI)/2)
angleRange=270;
//g.drawRect(originX, originY, squareLength, squareLength);
//g.fillArc(originX, originY, squareLength, squareLength, (int)angleOrigin, (int)angleRange);
for (int i=0; i<minorTick ; i++) {
angle=((i*angleRange/(minorTick-1))+angleOrigin)-7;
//System.out.println(angle*360/(2*Math.PI));
xTick= (int) (centerX+Math.cos(angle)*squareLength/2);
yTick= (int) (centerY-Math.sin(angle)*squareLength/2);
//g.drawLine(centerX, centerY, xTick, yTick);
//g.fillArc(originX, originY, squareLength, squareLength, (int)angle, (int)14);
}
*/
angleOrigin=-(Math.PI)/4;
angleRange=3*(Math.PI)/2;
g2D.setStroke(new BasicStroke(length/50+1));
for (int i=0; i<minorTick ; i++) {
angle=i*angleRange/(minorTick-1)+angleOrigin;
//System.out.println(angle*360/(2*Math.PI));
xTick= (int) (centerX+Math.cos(angle)*squareLength/2);
yTick= (int) (centerY-Math.sin(angle)*squareLength/2);
//g.drawLine(centerX, centerY, xTick, yTick);
g2D.draw (new Line2D.Float(centerX, centerY, xTick, yTick));
}
backgroundColor = this.getBackground();
g.setColor(backgroundColor);
diameter=(int)(length*0.8);
originX=(width-diameter)/2;
originY=(height-diameter)/2;
g.fillOval(originX, originY, diameter, diameter);
/*
RadialGradientPaint gp;
Point2D center= new Point2D.Float(width/2, height/2);
diameter=(int)(length*0.75);
originX=(width-diameter)/2;
originY=(height-diameter)/2;
float radius=diameter/2;
float[] dist = {0.7f, 1f};
Color[] colors = {Color.BLUE, Color.GRAY};
gp=new RadialGradientPaint(center, radius, dist, colors);
g2D.setPaint(gp);
g2D.fillOval(originX,originY,diameter,diameter);
*/
diameter=(int)(length*0.75);
originX=(width-diameter)/2;
originY=(height-diameter)/2;
g.setColor(Color.GRAY);
g.fillOval(originX,originY,diameter,diameter);
diameter=(int)(length*0.7);
originX=(width-diameter)/2;
originY=(height-diameter)/2;
g.setColor(knobColor);
g.fillOval(originX,originY,diameter,diameter);
g2D.setStroke(new BasicStroke(length/50+3));
angle=(2*Math.PI)*(0.75-cursorValue*0.75)+angleOrigin;
xCursor= (int) (centerX+Math.cos(angle)*length*0.35);
yCursor= (int) (centerY-Math.sin(angle)*length*0.35);
g.setColor(Color.GRAY);
g2D.draw (new Line2D.Float(centerX, centerY, xCursor, yCursor));
g2D.rotate(Math.toRadians(270.0));
g.setFont(new Font(g.getFont().getFontName(),Font.PLAIN,this.getHeight()/3));
titleWidth=g.getFontMetrics().stringWidth(title);
g2D.drawString(title,-this.getHeight()+titleWidth/3,this.getHeight()/4);
//System.out.println(titleWidth);
//System.out.println(this.getHeight()*(-70)/100+" - "+this.getHeight());
}
#Override
public void mouseClicked(MouseEvent arg0) {
// TODO Auto-generated method stub
//System.out.println("Bouton : "+arg0.getButton());
}
#Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent arg0) {
// TODO Auto-generated method stub
//System.out.println("Bouton : "+arg0.getButton());
PointerInfo pointer = MouseInfo.getPointerInfo();
Point mouseLocation = pointer.getLocation();
xMouseOrigin = (int) mouseLocation.getX();
yMouseOrigin = (int) mouseLocation.getY();
if (arg0.getButton()==MouseEvent.BUTTON1) {
mousePressed=true;
t= new Thread(new TrackPosition());
t.start();
} else if (arg0.getButton()==MouseEvent.BUTTON3) {
cursorValue=initialCursorValue;
repaint();
knobValue=cursorValue;
}
}
#Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
mousePressed=false;
//System.out.println("Mouse released");
repaint();
}
class TrackPosition implements Runnable {
#Override
public void run() {
// TODO Auto-generated method stub
while (mousePressed==true) {
PointerInfo pointer = MouseInfo.getPointerInfo();
Point mouseLocation = pointer.getLocation();
yMouse = (int) mouseLocation.getY();
yDeltaMouse=(float)(yMouse-yMouseOrigin)/100;
cursorValue=cursorValue+yDeltaMouse;
yMouseOrigin=yMouse;
if (cursorValue >=1) {
cursorValue=1;
} else if (cursorValue <= 0) {
cursorValue=0;
}
//This repaint is a problem if I "uncomment" it
repaint();
knobValue=cursorValue;
}
}
}
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
JFrame frame = new JFrame();
frame.setSize(300,70);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new Knob(0.5, new Color(0,255,0,255), 3, 9, "Gain"));
frame.setVisible(true);
}
}
You miss to call the paintComponent(Graphics g) method from JComponent.
Changing your code as below will solve the problem.
public void paintComponent(Graphics g) {
super.paintComponent(g);
Related
The following example draws 2 Rectangles.
Within the paintComponent() method, the first Rectangle is draw normally and the second Rectangle is rotated.
Rotation is based on the mouse movement. If the mouse is clicked on the rectangle and then moved
in a circular fashion, the 2nd Rectangle rotations as expected, but as the mouse rotations around, the rotation of the Rectangle isn't always in sync with the mouse.
I suspect this is all related to the angle calculation. Any suggestions on how to get the rotation of the Rectangle to be in sync with the mouse movement?
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.util.Vector;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class SimpleTest extends JComponent
{
static int x = 200,y = 200 , width = 100 , height = 30;
static Vector objectsToDraw = new Vector();
static int mouseClickX, mouseClickY, mouseX, mouseY = 0;
static double angle;
public static void main(String[] args)
{
//Create Frame
JFrame window = new JFrame();
//Attach Mouse Listeners.
window.addMouseMotionListener(new MouseMotionListener()
{
#Override
public void mouseMoved(MouseEvent e) { }
#Override
public void mouseDragged(MouseEvent e)
{
// System.out.println("Dragged");
System.out.println("Dragged at X :" + e.getX() + " Y : " + e.getY());
calculateAngle(e.getX(), e.getY());
mouseX = e.getX();
mouseY = e.getY();
window.repaint();
}
});
window.addMouseListener(new MouseListener()
{
#Override
public void mouseClicked(MouseEvent arg0) { }
#Override
public void mouseEntered(MouseEvent arg0) { }
#Override
public void mouseExited(MouseEvent arg0) { }
#Override
public void mousePressed(MouseEvent e)
{
System.out.println("Pressed at X :" + e.getX() + " Y : " + e.getY());
mouseClickX = e.getX();
mouseClickY = e.getY();
}
#Override
public void mouseReleased(MouseEvent arg0)
{
System.out.println("Released");
mouseClickX = 0;
mouseClickY = 0;
window.repaint();
}
});
//show Window
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setBounds(30, 30, 800, 800);
window.getContentPane().add(new SimpleTest());
window.setVisible(true);
}
public static void calculateAngle (int x, int y)
{
int deltaX = x - 250;//Rectangle Center X
int deltaY = y - 200;//Rectangle Center Y
angle = Math.toDegrees(Math.atan2(deltaY, deltaX));
System.out.println("Angle = " + angle);
}
#Override //Works
public void paintComponent(Graphics g)
{
System.out.println("paintComponent() - using angle : " + angle);
Graphics2D g2d = (Graphics2D)g;
AffineTransform old = g2d.getTransform();
g.drawRect(x, y, width, height);
g2d.rotate(Math.toRadians(angle), 250, 215);
Rectangle rect2 = new Rectangle(200, 200, 100, 30);
g.drawRect(x, y, width, height);
g2d.setTransform(old);
}
}
For one, you're adding the MouseListener to the wrong component. Don't add it to the window but rather to the JComponent that does the drawing. Otherwise, your mouse positioning will be off by however large the menu bar is, or any other components that changes the position of the drawing component (the component that uses the mouse positions) relative to the JFrame (your component that currently gets the mouse positions).
e.g.,
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import javax.swing.*;
#SuppressWarnings("serial")
public class SimpleTest2 extends JPanel {
// avoiding "magic" numbers
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
public static final int RECT_X = 200;
public static final int RECT_Y = RECT_X;
public static final int RECT_WIDTH = 100;
public static final int RECT_HEIGHT = 30;
private double angle;
private static void createAndShowGui() {
SimpleTest2 mainPanel = new SimpleTest2();
JFrame frame = new JFrame("Simple Test 2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack(); // let the GUI size itself
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
public SimpleTest2() {
// using an adapter is a nice clean way of avoiding empty method bodies
MouseAdapter myMouse = new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
calculateAngle(e.getX(), e.getY());
repaint();
}
#Override
public void mousePressed(MouseEvent e) {
calculateAngle(e.getX(), e.getY());
repaint();
}
};
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
}
public void calculateAngle(int x, int y) {
// get rid of "magic" numbers
int deltaX = x - (RECT_X + RECT_WIDTH / 2);// Rectangle Center X
int deltaY = y - (RECT_Y + RECT_HEIGHT / 2);// Rectangle Center Y
angle = Math.toDegrees(Math.atan2(deltaY, deltaX));
}
#Override
public Dimension getPreferredSize() {
// better way to size the drawing component
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g); // ***don't forget this guy***
Graphics2D g2d = (Graphics2D) g;
// for smoother rendering
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
AffineTransform old = g2d.getTransform();
g.drawRect(RECT_X, RECT_Y, RECT_WIDTH, RECT_HEIGHT);
g2d.rotate(Math.toRadians(angle), 250, 215);
// Rectangle rect2 = new Rectangle(200, 200, 100, 30);
g.drawRect(RECT_X, RECT_Y, RECT_WIDTH, RECT_HEIGHT);
g2d.setTransform(old);
}
}
I am writing this program that creates an H-Tree design wherever you click, and I am supposed to implement a JMenuBar so I can change the color of the recursive drawing. For some reason it is not showing up no matter what I do.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
#SuppressWarnings("serial") class Echo extends JFrame implements ActionListener, MouseListener, ChangeListener{
private static final int SLIDER_MIN = 1;
private static final int SLIDER_MAX = 11;
private static final int SLIDER_INIT = 1;
private int x;
private int y;
private int rec = SLIDER_INIT;
private Echo() {
super("H-Tree Drawing Pad");
Container canvas = this.getContentPane();
addMenus();
canvas.add(createSlider(), BorderLayout.SOUTH);
this.setSize(800,800);
this.setVisible(true);
this.setLocation(200, 200);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.addMouseListener(this);
}
private void addMenus() {
JMenuBar menuBar = new JMenuBar();
this.setJMenuBar(menuBar);
JMenu menuColor = new JMenu("Color");
menuBar.add(menuColor);
JMenuItem mitToBlack = new JMenuItem("Black");
mitToBlack.addActionListener(new ColorListener());
menuColor.add(mitToBlack);
JMenuItem mitToBlue = new JMenuItem("Blue");
mitToBlue.addActionListener(new ColorListener());
menuColor.add(mitToBlue);
JMenuItem mitToRed = new JMenuItem("Red");
mitToRed.addActionListener(new ColorListener());
menuColor.add(mitToRed);
JMenuItem mitToGreen = new JMenuItem("Green");
mitToGreen.addActionListener(new ColorListener());
menuColor.add(mitToGreen);
}
private JPanel createSlider() {
JPanel slider = new JPanel(new BorderLayout());
JSlider electricSlide = new JSlider(JSlider.HORIZONTAL,SLIDER_MIN, SLIDER_MAX, SLIDER_INIT);
electricSlide.addChangeListener(this);
electricSlide.setMajorTickSpacing(2);
electricSlide.setMinorTickSpacing(1);
electricSlide.setPaintTicks(true);
electricSlide.setPaintLabels(true);
electricSlide.setBorder(BorderFactory.createEmptyBorder(0,0,10,0));
slider.add(electricSlide);
return slider;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Echo();
}
});
}
#Override
public void mouseClicked(MouseEvent e) {
x = e.getX();
y = e.getY();
repaint();
}
#Override
public void paint(Graphics g) {
g.setColor(Color.BLACK);
drawOrder(this.getRec(),x,y,95,g);
}
private void drawHTree(int x, int y, int size, Graphics g) {
g.drawLine(x, y, x, y + size);
g.drawLine(x + size, y, x + size, y + size);
g.drawLine(x, y + size / 2, x + size, y + size / 2);
}
private void drawOrder(int initiative, int x, int y, int size, Graphics g) {
this.drawHTree(x, y, size, g);
if(initiative > 1) {
this.drawOrder(initiative - 1, x - size/4, y - size/4, size/2, g);
this.drawOrder(initiative - 1, x+size - size/4, y-size/4, size/2, g);
this.drawOrder(initiative -1, x-size/4, y+size-size/4, size/2, g);
this.drawOrder(initiative - 1, x+size-size/4, y+size-size/4, size/2, g);
}
}
#Override
public void stateChanged(ChangeEvent e) {
JSlider origin = (JSlider)e.getSource();
if(!origin.getValueIsAdjusting()) {
setRec((int)origin.getValue());
}
}
public int getRec() {
return rec ;
}
public void setRec(int rec) {
this.rec = rec;
}
private class ColorListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent arg0) {
}
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
}
You've overridden the paint method of your JFrame and did not call the super's method within it, breaking the painting chain.
e.g., you're missing this:
#Override
public void paint(Graphics g) {
super.paint(g); // ****** you're missing this ***
g.setColor(Color.BLACK);
drawOrder(this.getRec(), x, y, 95, g);
}
By not calling the super's painting method your code is now preventing the JFrame's child components, including its menu bar, from painting correctly .
Having said this, in fact you should never paint directly within a JFrame but rather in a JPanel's paintComponent and even there you should call the super's painting method, here super.paintComponent(g);.
I'm a Computer Science student who uses a mid-2009 MacBook Pro running OS X Yosemite 10.10.3. I recently had a class activity in my Object-Oriented programming class where we went step-by-step in creating an interactive Java program in which the user simply clicks a football and watch it get kicked over the goalpost with a green background.
However, even though my Java code matched the code of my classmates' Windows computers with no syntax errors, there are some problems with my program properly running while theirs work perfectly fine:
While the window for the application opens with the title and green background, neither the football nor goalpost is displayed. However, if I manually stretch the window, they show back up. I've tried changing the window dimensions to see if that was causing it to no avail.
When the football is clicked, instead of moving towards the goalpost as intended, both the football and goalpost vanish and don't return. Only the green background is displayed, even when I try manually stretching the window again.
I still submitted the code to my instructor, which worked fine on his computer (he doesn't understand the problem either since he doesn't use OS X). I tried to run the code on two other IDE's to see if Eclipse was the problem, but they all produced the same results. If this is an OS X or computer-exclusive problem, how am I able to get around this?
This is my current code:
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class Football extends JFrame {
final int WINDOW_WIDTH = 800;
final int WINDOW_HEIGHT = 400;
private int x = 40; // Ball's X coordinate
private int y = 300; // Ball's Y coordinate
private final int WIDTH = 35; // Ball's width
private final int HEIGHT = 60; // Ball's height
private final int X_MOVE = 14; // Pixels to move ball
private final int Y_MOVE = 4;
private final int TIME_DELAY = 25; // Time delay
private Timer timer; // Timer object
/**
init method
*/
public Football() {
setTitle("Football");
setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
// Set Background to a Dark Green
getContentPane().setBackground(new Color(0, 220, 50));
// initTimer();
addMouseListener(new FbMouseListener());
}
public void paint(Graphics g)
{
// Call the superclass paint method.
super.paint(g);
// Set the color to Brown
g.setColor(new Color(129, 74, 25));
// Draw the football
g.fillOval(x, y, WIDTH, HEIGHT);
// Draw the Goalpost
g.setColor(Color.YELLOW);
g.fillRect(670, 240, 5, 140);
g.fillRect(610, 80, 5, 140);
g.fillRect(740, 120, 5, 140);
// Need Thicker line
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(5));
g2.drawLine(612, 220, 742, 260);
}
private class TimerListener implements ActionListener
{
public void actionPerformed(ActionEvent e) {
// Update the ball's position
y -= Y_MOVE;
x += X_MOVE;
// Force a call to the paint method
repaint();
}
}
public void initTimer()
{
timer = new Timer(TIME_DELAY, new TimerListener());
timer.start();
}
private class FbMouseListener implements MouseListener
{
public void mouseClicked(MouseEvent e)
{
if (e.getX() >= x && e.getX() <= (x + WIDTH) && e.getY() >= y && e.getY() <= (y + HEIGHT))
{
initTimer();
}
}
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
}
public static void main(String[] args)
{
Football fb = new Football();
}
}
Any help or suggestions would be appreciated, as I would like to make sure this doesn't affect any future programs I create.
Generally, overriding paint of a top level container like JFrame is a bad idea, JFrame contains a bunch of child components that can be painted independently of the parent, which seems to be the case here
As you can see, there are (at least) 3 other components in between the frame and the user
Generally, you should create a custom class which extends from something like JPanel and override it's paintComponent and perform your custom painting there.
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Football {
public static void main(String[] args) {
new Football();
}
public Football() {
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("Football");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new FootballPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class FootballPane extends JPanel {
public static final int WINDOW_WIDTH = 800;
public static final int WINDOW_HEIGHT = 400;
private int x = 40; // Ball's X coordinate
private int y = 300; // Ball's Y coordinate
private static final int WIDTH = 35; // Ball's width
private static final int HEIGHT = 60; // Ball's height
private static final int X_MOVE = 14; // Pixels to move ball
private static final int Y_MOVE = 4;
private static final int TIME_DELAY = 25; // Time delay
private Timer timer; // Timer object
/**
* init method
*/
public FootballPane() {
// Set Background to a Dark Green
setBackground(new Color(0, 220, 50));
// initTimer();
addMouseListener(new FbMouseListener());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(WINDOW_WIDTH, WINDOW_HEIGHT);
}
#Override
protected void paintComponent(Graphics g) {
// Call the superclass paint method.
super.paintComponent(g);
// Set the color to Brown
g.setColor(new Color(129, 74, 25));
// Draw the football
g.fillOval(x, y, WIDTH, HEIGHT);
// Draw the Goalpost
g.setColor(Color.YELLOW);
g.fillRect(670, 240, 5, 140);
g.fillRect(610, 80, 5, 140);
g.fillRect(740, 120, 5, 140);
// Need Thicker line
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(5));
g2.drawLine(612, 220, 742, 260);
}
private class TimerListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
// Update the ball's position
y -= Y_MOVE;
x += X_MOVE;
// Force a call to the paint method
repaint();
}
}
public void initTimer() {
timer = new Timer(TIME_DELAY, new TimerListener());
timer.start();
}
private class FbMouseListener implements MouseListener {
public void mouseClicked(MouseEvent e) {
if (e.getX() >= x && e.getX() <= (x + WIDTH) && e.getY() >= y && e.getY() <= (y + HEIGHT)) {
initTimer();
}
}
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
}
}
}
See Painting in AWT and Swing and Performing Custom Painting for more details
I'm writing a program to transform, rotate and scale a 2d square. I have the transformation and rotation working but I need help with the scaling. I can't seem to find any help on the internet to help since I have to use a math equation and I can't find the equation needed. Just so you know I don't want to use gl.glScaled(). I need to use a math equation but I can't figure it out.
package lab2;
public class Square {
public double [][] vertices = new double[4][2];
public Square(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
{
vertices[0][0]=x1;
vertices[0][1]=y1;
vertices[1][0]=x2;
vertices[1][1]=y2;
vertices[2][0]=x3;
vertices[2][1]=y3;
vertices[3][0]=x4;
vertices[3][1]=y4;
}
public double area()
{
return (vertices[1][0]-vertices[0][0])*(vertices[1][0]-vertices[0][0])+(vertices[1][1]-vertices[0][1])*(vertices[1][1]-vertices[0][1]);
}
public void translate(double tx, double ty)
{
for(int i=0;i<vertices.length;i++)
{
vertices[i][0]+=tx;
vertices[i][1]+=ty;
}
}
public void rotate(double theta)
{
//double x =0;
double x = (vertices[0][0]+vertices[2][0])/2;
double y = (vertices[0][1]+vertices[2][1])/2;
double oldX;
int i;
for(i = 0; i < 4; i++){
oldX = vertices[i][0];
vertices[i][0] = x + (vertices[i][0]-x)*Math.cos(theta*0.0174532925199)-(vertices[i][1]- y)*Math.sin(theta*0.0174532925199);
vertices[i][1] = y + (oldX-x)*Math.sin(theta*0.0174532925199)+(vertices[i][1]-y)*Math.cos(theta*0.0174532925199);
}
}
public void scale(double sx, double sy)
{
}
}
Then I also have this SquareControl.java so that I can control how I want it to work so for this program I'll be using key events
package lab2;
import java.awt.Frame;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.media.opengl.*;
import javax.media.opengl.awt.GLCanvas;
import com.sun.opengl.util.Animator;
import com.sun.opengl.util.FPSAnimator;
public class Square_Control implements GLEventListener, KeyListener {
Square square = new Square(100,100,200,100,200,200,100,200);
boolean rotating = false;
boolean scaling = false;
boolean enlarge = true;
double theta = 1;
double sx = 1.01, sy = 1.01;
GLProfile glp;
GLCapabilities caps;
GLCanvas canvas;
public Square_Control()
{
glp = GLProfile.getDefault();
caps = new GLCapabilities(glp);
canvas = new GLCanvas(caps);
Frame frame = new Frame("AWT Window Test");
frame.setSize(300, 300);
frame.add(canvas);
frame.setVisible(true);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
canvas.addGLEventListener(this);
canvas.addKeyListener(this);
canvas.requestFocus();
Animator animator = new FPSAnimator(canvas,60);
animator.add(canvas);
animator.start();
}
public static void main(String[] args) {
Square_Control sqc = new Square_Control();
}
public void update()
{
if (rotating)
square.rotate(theta);
if (scaling)
{
square.scale(1, 1);
}
}
#Override
public void display(GLAutoDrawable drawable) {
update();
GL2 gl = drawable.getGL().getGL2();
gl.glClear(GL.GL_COLOR_BUFFER_BIT);
gl.glColor3f(1, 0, 0);
gl.glBegin(GL.GL_LINE_LOOP);
gl.glVertex2d(square.vertices[0][0], square.vertices[0][1]);
gl.glVertex2d(square.vertices[1][0], square.vertices[1][1]);
gl.glVertex2d(square.vertices[2][0], square.vertices[2][1]);
gl.glVertex2d(square.vertices[3][0], square.vertices[3][1]);
gl.glEnd();
}
#Override
public void dispose(GLAutoDrawable drawable) {
// TODO Auto-generated method stub
}
#Override
public void init(GLAutoDrawable drawable) {
// TODO Auto-generated method stub
GL2 gl = drawable.getGL().getGL2();
gl.glMatrixMode(gl.GL_PROJECTION);
gl.glLoadIdentity();
gl.glOrtho(0, 300, 0, 300, -1, 1);
gl.glViewport(0, 0, 300, 300);
}
#Override
public void reshape(GLAutoDrawable drawable, int x, int y, int width,
int height) {
// TODO Auto-generated method stub
}
#Override
public void keyPressed(KeyEvent arg0) {
if (arg0.getKeyCode() == KeyEvent.VK_RIGHT)
square.translate(4, 0);
else if (arg0.getKeyCode() == KeyEvent.VK_LEFT)
square.translate(-4, 0);
else if (arg0.getKeyCode() == KeyEvent.VK_UP)
square.translate(0, 4);
else if (arg0.getKeyCode() == KeyEvent.VK_DOWN)
square.translate(0, -4);
//also add code to toggle rotation/scaling
if(arg0.getKeyCode() == KeyEvent.VK_R)
{
rotating = !rotating;
}
if(arg0.getKeyCode() == KeyEvent.VK_S)
{
scaling = !scaling;
}
}
#Override
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
}
To scale an object, you simply have to multiply each vertex by your scaling factor. A scaling factor of 1.0 will do nothing while a scaling factor of 2.0 will double de position of each vertex, hence scaling it AND probably translating it.
If you want the object to stay in place, you'll have to first translate it to your object's center, scale, then translate back to it's original position.
That is if you want to do the job software.
You should consider using Matrix functions. glRotatef, glTranslatef and glScalef.
Please bear with me, english is neither my first nor my second language.
The basic idea is to load images and draw a few shapes using the mouse.
I created these three java classes (see code at the botoom end):
1 Main, right now this is only used to initiate the second class "Action"
2 Action, this class should contain every action the user performs on the ui (drawing, loading images...)
3 UI, this class creates the UI and holds every object concerning the UI (Jlabels, JButtons, JFrame ....)
At this stage it (kinda) works as intended but i have still a few questions. I tried to visualise every problem but i can only use 2 images in this post, so please use this image.
1) Drawing shapes works as itended but as soon as the user creates (releases the mousebutton) the shape it should be displayed in blue colours. This doesn't work unless a new shape is drawn. How can i update the shape after the user releases the button? As seen in the first and second frame of the linked image.
-- solved
added a call to paint(); in the mouseReleased event.
2) The first shape is darker/a fuller colour than the rest of the shapes, see second and fifth frame in the linked image. I am not sure how to correct this one, any ideas?
--solved
exchanged this two lines
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
g2d.fill(shape);
3) None of the shapes are visiable after resizing the UI, as seen in the third and foruth frame of the linked image. How do i force the program to draw them?
-- solved
added ComponentListener to the JFrame, now calling paint() in the componentResized() event
3.1) How do i get the shapes to stay within the original image (the black square) after resizing? Fourth frame of the linked image.
-- solved
i forgot to add an offset if the frame was resized....
4) Checking if the mouse is still within the borders of the image works only if the user doesn't use the scrollpanes. Does anyone have a better idea to check if the mouse is within the image borders?
5) The shapes flicker during drawing if there is more than one shape, i guess because they are drawn everytime the user drags the mouse while drawing. How to fix this problem?
Don't hesitate to point out anything else that is wrong or really the best coding practice.
Thank you in advance.
update
-added titels
-changed formating
-added the described changes
Main class
public class Main {
public static void main(String[] args) {
new Action();
}
}
UIclass
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
public class UI {
public JLabel jlabel_image;
public JFrame jframe_ui;
public JLabel jlabel_info;
private Action action;
private JScrollPane jscrollpane_image;
public UI(Action action) {
this.action = action;
jlabel_image = new JLabel();
jlabel_image.setHorizontalAlignment(jlabel_image.CENTER);
jlabel_image.setVerticalAlignment(jlabel_image.CENTER);
jlabel_image.addMouseListener(action);
jlabel_image.addMouseMotionListener(action);
jscrollpane_image = new JScrollPane(jlabel_image);
jlabel_info = new JLabel("mouse: ");
jframe_ui = new JFrame("test frame");
jframe_ui.addComponentListener(action);
jframe_ui.setPreferredSize(new Dimension(500,500));
jframe_ui.getContentPane().setLayout(new BorderLayout());
jframe_ui.setMinimumSize(new Dimension(500,500));
jframe_ui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe_ui.add(jscrollpane_image, BorderLayout.CENTER);
jframe_ui.add(jlabel_info, BorderLayout.SOUTH);
jframe_ui.pack();
jframe_ui.setVisible(true);
}
}
Action class
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.ImageIcon;
public class Action implements ActionListener, ComponentListener, MouseListener, MouseMotionListener {
private UI ui;
private Point startpoint;
private Point endpoint;
private ArrayList<Shape> shapes;
private boolean mouseonimage;
public Action() {
ui = new UI(this);
shapes = new ArrayList<Shape>();
mouseonimage = false;
BufferedImage bufferedimage = new BufferedImage(800, 800, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = bufferedimage.createGraphics();
g2.setPaint(Color.BLACK);
g2.fillRect(0, 0, 800, 800);
g2.dispose();
ui.jlabel_image.setIcon(new ImageIcon(bufferedimage));
}
#Override
public void componentHidden(ComponentEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void componentMoved(ComponentEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void componentResized(ComponentEvent arg0) {
// TODO Auto-generated method stub
paint();
}
#Override
public void componentShown(ComponentEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseDragged(MouseEvent me) {
// TODO Auto-generated method stub
if(mouseonimage) {
if(checkMouseCoordinates(me.getPoint())) {
endpoint = me.getPoint();
paint();
}
}
}
#Override
public void mouseMoved(MouseEvent me) {
// TODO Auto-generated method stub
checkMouseCoordinates(me.getPoint());
}
#Override
public void mouseClicked(MouseEvent me) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent me) {
// TODO Auto-generated method stub
mouseonimage = true;
checkMouseCoordinates(me.getPoint());
}
#Override
public void mouseExited(MouseEvent me) {
// TODO Auto-generated method stub
mouseonimage = false;
checkMouseCoordinates(me.getPoint());
}
#Override
public void mousePressed(MouseEvent me) {
// TODO Auto-generated method stub
if(mouseonimage) {
if(me.getButton() == MouseEvent.BUTTON1) {
if(checkMouseCoordinates(me.getPoint())) {
startpoint = me.getPoint();
endpoint = me.getPoint();
}
}
}
}
#Override
public void mouseReleased(MouseEvent me) {
// TODO Auto-generated method stub
if(checkMouseCoordinates(me.getPoint())) {
endpoint = me.getPoint();
}
if(startpoint != null) {
if(Math.abs(startpoint.x - endpoint.x) > 9) {
if(Math.abs(startpoint.y - endpoint.y) > 9) {
shapes.add(createrectangle(startpoint, endpoint));
}
}
}
startpoint = null;
endpoint = null;
paint();
}
#Override
public void actionPerformed(ActionEvent me) {
// TODO Auto-generated method stub
}
public boolean checkMouseCoordinates(Point point) {
boolean withinBoarders = false;
if(mouseonimage) {
int imageFrameWidth = ui.jlabel_info.getBounds().width;
int imageFrameHeight = ui.jlabel_info.getBounds().y;
int imageWidth = ui.jlabel_image.getIcon().getIconWidth();
int imageHeight = ui.jlabel_image.getIcon().getIconHeight();
int x = 0;
int y = 0;
int w = imageFrameWidth;
int h = imageFrameHeight;
System.out.println(ui.jlabel_info.getBounds());
if(imageFrameWidth > imageWidth) {
x = (imageFrameWidth - imageWidth)/2 - 9;
w = imageWidth;
}
if(imageFrameHeight > imageHeight) {
y = (imageFrameHeight - imageHeight)/2;
h = imageHeight;
if(imageFrameWidth > imageWidth) {
x = x + 7;
}
if(imageFrameHeight > imageHeight) {
y = y - 1;
}
}
Rectangle imageFrame = new Rectangle(x, y, w, h);
if(imageFrame.contains(point)) {
withinBoarders = true;
ui.jlabel_info.setText(String.format("mouse: on image :: %d/%d :: %d/%d :: %d/%d||%d/%d",point.x, point.y, imageFrameHeight, imageFrameWidth, imageFrame.x, imageFrame.width, imageFrame.y, imageFrame.height));
} else {
ui.jlabel_info.setText(String.format("mouse: not on image %d/%d",point.x, point.y));
}
} else {
ui.jlabel_info.setText("mouse: not on image");
}
return withinBoarders;
}
private Shape createrectangle(Point start, Point end) {
int image_x = (ui.jlabel_image.getWidth() - ui.jlabel_image.getIcon().getIconWidth()) / 2;
int image_y = (ui.jlabel_image.getHeight() - ui.jlabel_image.getIcon().getIconHeight()) / 2;
int image_w = ui.jlabel_image.getIcon().getIconWidth();
int image_h = ui.jlabel_image.getIcon().getIconHeight();
int x = start.x;
int y = start.y;
int h = end.y;
int w = end.x;
x = x < image_x ? image_x : x;
x = x > (image_x + image_w - 1) ? (image_x + image_w - 1) : x;
w = w < image_x ? image_x : w;
w = w > (image_x + image_w - 1) ? (image_x + image_w - 1) : w;
y = y < image_y ? image_y : y;
y = y > (image_y + image_h - 1) ? (image_y + image_h - 1) : y;
h = h < image_y ? image_y: h;
h = h > (image_y + image_h - 1) ? (image_y + image_h - 1) : h;
return new Rectangle2D.Float(Math.min(x, w), Math.min(y, h), Math.abs(x - w), Math.abs(y - h));
}
private void paint() {
Graphics g;
g = ui.jlabel_image.getGraphics();
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(new BufferedImage(ui.jlabel_image.getIcon().getIconWidth(), ui.jlabel_image.getIcon().getIconHeight(), BufferedImage.TYPE_INT_RGB), (ui.jlabel_image.getWidth() - ui.jlabel_image.getIcon().getIconWidth()) / 2, (ui.jlabel_image.getHeight() - ui.jlabel_image.getIcon().getIconHeight()) / 2, null);
for(Shape shape: shapes) {
g2d.setPaint(Color.BLUE);
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
g2d.fill(shape);
g2d.setPaint(Color.BLUE);
g2d.draw(shape);
}
if(startpoint != null) {
if(endpoint != null) {
g2d.setPaint(Color.RED);
g2d.draw(createrectangle(startpoint, endpoint));
}
}
}
}
For the fist problem you have to repaint in order to see the changes, just add a single line:
#Override
public void mouseReleased(MouseEvent me) {
endpoint = me.getPoint();
if(Math.abs(startpoint.x - endpoint.x) > 9) {
if(Math.abs(startpoint.y - endpoint.y) > 9) {
shapes.add(createrectangle(startpoint, endpoint));
}
}
// Add this line
paint();
startpoint = null;
endpoint = null;
}
The second problem, I think is caused because you are telling the graphics to composite the
destination (i.e, the graphics of the image you already have) to have an alpha value of 0.5.
Try to change this code:
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
for this
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
Right now I don't have time, but later I can help you. Also I think there are better ways of achieving that. For example using a Jpanel instead of a Jlabel or an ImageIcon