I'm working on a little java project wherein I need to draw a small handful of shapes to the screen. I also need to capture mouse events for utility functions. Right now, I have a program that captures a click, and sends out a little ripple from that position. Whenever the mouse is moved, however, the paintComponent(Graphics g) method just stops entirely. I thought it was a buffering issue, so I implemented one, and it just slowed even more at normal rendering. Another strange thing: When the mouse is being held down, the paintComponent method doesn't care about the mouse movement anymore. It resumes as normal.
EDIT: I should also mention that the mouse events are triggered by a class that implements MouseListener. They are calling correctly. To get the mouse position, I use this:
public Point getMouse() {
Point mousePos = MouseInfo.getPointerInfo().getLocation();
SwingUtilities.convertPointFromScreen(mousePos, screen);
return mousePos;
}
package com.noneofyebidmness;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.util.ArrayList;
import javax.swing.JPanel;
import com.noneofyebidmness.Organism;
#SuppressWarnings("serial")
public class Screen extends JPanel {
private int diameter = 10;
private ArrayList<Point> ripplePositions;
private ArrayList<Point> rippleDiameters;
public Screen() {
ripplePositions = new ArrayList<Point>();
rippleDiameters = new ArrayList<Point>();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(Reference.SCREEN_WIDTH_DEFAULT, Reference.SCREEN_HEIGHT_DEFAULT);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("Painting");
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for(Organism o : Wrapper.getInstance().getOrganisms()) {
o.draw(g2d);
}
Point mouse = Wrapper.getInstance().getMouse();
Utility.background(Utility.color(0, 150, 250), g2d, this);
g2d.setColor(Utility.color(255));
g2d.fillOval((int)mouse.getX() - diameter / 2, (int)mouse.getY() - diameter / 2, diameter, diameter);
g2d.setColor(Color.BLACK);
g2d.drawString(Wrapper.FPS_PERIODIC + "", 10, 20);
for(int i = 0; i < ripplePositions.size(); i++) {
int w = rippleDiameters.get(i).x;
g2d.setColor(Utility.color(255, Math.min(220-w, 255)));
g2d.setStroke(new BasicStroke(10));
g2d.drawOval(ripplePositions.get(i).x - w / 2, ripplePositions.get(i).y - w / 2, rippleDiameters.get(i).x, rippleDiameters.get(i).x);
if(w < 255) {
rippleDiameters.get(i).x += rippleDiameters.get(i).y;
if(rippleDiameters.get(i).y > 0)
rippleDiameters.get(i).y -= 0.01;
}
if(220-w == 0) {
ripplePositions.remove(i);
rippleDiameters.remove(i);
}
}
g2d.dispose();
}
public void addRipple() {
ripplePositions.add(Wrapper.getInstance().getMouse());
rippleDiameters.add(new Point(diameter, 20));
}
}
TL;DR:
When the mouse is moved over my JPanel, it stops calling paintComponent().
If the mouse is held down and moved, things operate as normal.
Any help is appreciated,
Carl Litchman
Related
I try to do my first graphical program in java - bouncing ball. Actually it should be brick breaker according to tutorial. But for now it is bouncing ball. The ball should bounce from the frames of the window. So far I programmed it. The issue is that the ball is laggy, it does not move fluently at all. If I press and hold a keyboard key the ball moves fluently. Firstly I though it might by problem with my graphical card and linux system(AMD Ryzen 5 integrated graphic, Ubuntu 20.04)? But why it works correctly when I press and hold a keyboard button. Any ideas?
package com.company;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
JFrame obj=new JFrame();
Gameplay gamePlay = new Gameplay();
obj.setBounds(10,10,700,600);
obj.setTitle("Breakout Ball");
obj.setResizable(false);
obj.setVisible(true);
obj.setDefaultCloseOperation(obj.EXIT_ON_CLOSE);
obj.add(gamePlay);
}
}
package com.company;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Gameplay extends JPanel implements ActionListener {
private boolean play=false;
private int totalBrics=21;
private Timer timer;
private int delay=8;
private int playerX=310;
private int ballposX=120;
private int ballposY=350;
private int ballXdir=-1;
private int ballYdir=-2;
public Gameplay(){
setFocusable(true);
setFocusTraversalKeysEnabled(false);
timer=new Timer(delay, this);
timer.start();
}
public void paint (Graphics g){
//background
g.setColor(Color.black);
g.fillRect(1,1,692,592);
//borders
g.setColor(Color.yellow);
g.fillRect(0,0,3,592);
g.fillRect(0,0,692,3);
g.fillRect(691,0,3,592);
// paddle
g.setColor(Color.green);
g.fillRect(playerX, 550,100,8);
//ball
g.setColor(Color.yellow);
g.fillOval(ballposX, ballposY,20,20);
//g.dispose();
}
public void actionPerformed(ActionEvent f) {
//timer.start();
play=true;
if (play){
if(new Rectangle(ballposX, ballposY, 20,20).intersects(new Rectangle(playerX,550,100,8))){
ballYdir=-ballYdir;
}
ballposX+=ballXdir;
ballposY+=ballYdir;
if(ballposX<0){
ballXdir=-ballXdir;
}
if(ballposY<0){
ballYdir=-ballYdir;
}
if(ballposY>570){
ballYdir=-ballYdir;
}
if(ballposX>670){
ballXdir=-ballXdir;
}
}
repaint();
}
}
I ran your exact code and it worked fine. There was no real lag or some better behavior while pressing a key. Hence there is a problem with you hardware maybe?
I did not notice any lag but you were doing several things incorrectly which I have fixed.
You should be overriding paintComponent and not paint for a JPanel
You should invoke super.paintComponent as the first statement. When you do that it will then set the background color for you and clear the screen each time. It also does some other processing in the parent paintComponent method.
I also set the size for the JPanel which made the yellow borders appear (although a little off on the right). And the paddle was raised off the bottom edge a tad.
I then packed the frame and centered it on the screen.
I used RenderingHints via Graphics2D to smooth out the edges of the graphics.
All in all you did a pretty nice job. I am not certain why you would be having a delay. You might want to try increasing your x and y increment values and slowing down the timer. But it worked fine for me as is.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
class Gameplay extends JPanel implements ActionListener {
private boolean play = false;
private int totalBrics = 21;
private Timer timer;
private int delay = 8;
private int playerX = 310;
private int ballposX = 120;
private int ballposY = 350;
private int ballXdir = -1;
private int ballYdir = -2;
public static void main(String[] args) {
JFrame obj = new JFrame();
Gameplay gamePlay = new Gameplay();
gamePlay.setPreferredSize(new Dimension(700,600));
obj.setTitle("Breakout Ball");
obj.setResizable(false);
obj.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
obj.add(gamePlay);
gamePlay.setBackground(Color.black);
obj.pack();
// center on screen
obj.setLocationRelativeTo(null);
obj.setVisible(true);
}
public Gameplay() {
setFocusable(true);
setFocusTraversalKeysEnabled(false);
timer = new Timer(delay, this);
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// borders
g2d.setColor(Color.yellow);
g2d.fillRect(0, 0, 3, 592);
g2d.fillRect(0, 0, 692, 3);
g2d.fillRect(691, 0, 3, 592);
// paddle
g2d.setColor(Color.green);
g2d.fillRect(playerX, 550, 100, 8);
// ball
g2d.setColor(Color.yellow);
g2d.fillOval(ballposX, ballposY, 20, 20);
g2d.dispose();
}
public void actionPerformed(ActionEvent f) {
// timer.start();
play = true;
if (play) {
if (new Rectangle(ballposX, ballposY, 20, 20).intersects(
new Rectangle(playerX, 550, 100, 8))) {
ballYdir = -ballYdir;
}
ballposX += ballXdir;
ballposY += ballYdir;
if (ballposX < 0) {
ballXdir = -ballXdir;
}
if (ballposY < 0) {
ballYdir = -ballYdir;
}
if (ballposY > 570) {
ballYdir = -ballYdir;
}
if (ballposX > 670) {
ballXdir = -ballXdir;
}
}
repaint();
}
}
Problem solved,
just found answer here:Java window lagging on Ubuntu but not windows when code isn't lagging
Problem is with Linux's graphics scheduling, adding
Toolkit.getDefaultToolkit().sync();
after
repaint();
caused, that everything works fluently now.
I have a GUI with a panel where I draw. Whatever mouse pattern I do is repeated in every sector which is divided by two lines. However, I am able to do this because my paintComponen method does not invoke super.paintComponent. If I actually do invoke the method I get only a single point whenever i drag my mouse. How should i go about it?
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import javax.swing.JPanel;
public final class DisplayPanel extends JPanel
{
private boolean dragging;
private Point draw;
private Line2D sectorLine;
private int sectors;
public void init()
{
DisplayListener listener = new DisplayListener();
addMouseListener(listener);
addMouseMotionListener(listener);
setOpaque(true);
setBackground(Color.BLACK);
setSize(900,900);
setVisible(true);
}
//performs the drawing on the display panel
public void paintComponent(Graphics g)
{
//super.paintComponent(g);
setBackground(Color.BLACK);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.RED);
sectorLine = new Line2D.Double(getWidth()/2, 0, getWidth()/2, getHeight());
sectors = 12;
//draws the sectors on the screen
for(int i=0; i<sectors; i++)
{
g2d.draw(sectorLine);
g2d.rotate(Math.toRadians(30),getWidth()/2,getHeight()/2);
}
//draws the doilie
if(dragging)
{
for(int i=0; i<sectors; i++)
{
g2d.fillOval((int) draw.getX(), (int) draw.getY(),20, 20);
g2d.rotate(Math.toRadians(30), getWidth()/2, getHeight()/2);
}
}
}
private class DisplayListener extends MouseAdapter
{
public void mouseDragged(MouseEvent event)
{
dragging = true;
draw = event.getPoint();
repaint();
}
public void mouseReleased(MouseEvent event)
{
dragging = false;
}
}
}
super.paintComponent() erases/clears the area before drawing, so you only see the point you current draw.
If you want to draw the line the mouse dragged, you would have to store each drawn coordinate in a List, and then in paintComponent(), draw all the points again. Please be aware that this list could get very big, and thus eat a lot of memory, so you should probably think about limiting it somehow.
So I'm trying to draw a curve that has a taper profile like this : y=
Since I'm using awt.Graphics2D, I only have the ability to draw shapes.
One way to draw curves is to draw curves is use use BĀ“ezier curves. Is there a way convert this or a awt trick I'm not familiar with ?
One option to draw a curve is point by point :
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Curve extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.RED);
for (int x = 1; x <= 100; x++) {
int y = getY(x);
//2g does not support drawing a point so you draw a line
g2d.drawLine(x, y, x, y);
}
}
/**
*#param x
*#return
*/
private int getY(int x) {
//change to the function you want
return 50+ (100/(1+ (3* x)));
}
public static void main(String[] args) {
Curve points = new Curve();
JFrame frame = new JFrame("Curve");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(points);
frame.setSize(350, 300);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
How do I draw the unique rectangle(cube) inside the oval:
http://i.stack.imgur.com/pmHTl.jpg
For me it is tricky to use graphics to draw a rectangle such as the one shown. Any advice on what to do.
Ok. I will try to make myself as clear as I can. What I have so far are the orange ovals and the slim gray oval that is behind it. I only need to create one of these "dots" in a class and i will make many objects of them. The Task I need help in is drawing the "rectangle" shape you see that is in the orange dot using J Component possibly. By request I will add a picture of what I have so far if this edit does not fulfill the need for you to understand my problem.
Thanks
Edit : Here is the Code that I have for creating the ovals if that is of interest to you-
public void paint(Graphics g) {
Color c = (Color.orange);
g.setColor(Color.gray);
g.fillOval(3,3,60,60);
g.setColor(c);
g.fillOval(0,0,60,60);
}
Edit: My Attempt at an SSCCE -->
NanoBot Class(Where I am Creating the Bot in paint)
/**
* #author (Omar Ahmed)
*/
import javax.swing.*;
import java.awt.*;
public class NanoBot extends Image
{
public NanoBot(int x, int y, int w, int h)
{
super(x,y,w,h);
}
public void paint(Graphics g) {
Color c = (Color.orange);
g.setColor(Color.gray);
g.fillOval(3,3,60,60);
g.setColor(c);
g.fillOval(0,0,60,60);
//g.setColor(Color.black);
//g.fillOval(10,20,10,10);
//g.fillOval(40,20,10,10);
}
}
And The Driver:
/** Bot Swarm
* Date: May, 2013
* Author: Omar Ahmed
*/
import java.awt.*;
import javax.swing.*;
public class Driver {
private JFrame win;
private NanoBot bot1;
public Driver() {
win = new JFrame(" Swarm ");
win.setLayout(null);
win.setVisible(true);
win.setBounds( 20, 20, 800, 700);
win.getContentPane().setBackground(Color.white);
bot1=new NanoBot(50,50,70,70);
win.add(bot1,0);
}
Hope This Helps
Your first step is to break down your requirements...
You need to draw 3 shapes, front, top, side.
The front's y position is offset by 0.412 of the overall height. It's width is 0.77 of the overall width.
The top's height is 0.412 of the overall height, and it has a horizontal inset of 0.2 of the overall width...
The side's x position is offset by 0.77 of the overall width and has an inset of 0.47 of the over all width.
This is all very important, as you want to ensure that the shapes can resize reasonably well...
Now, you could simply use Graphics#drawLine and Graphics#drawRectangle to build the shape, but that, to me, is a lot of work...
Instead the 2D Graphics is very powerful and contains many wonderful things, the one of interest today is the Shape API, which allows you to define many different shapes, which can be drawn or filled.
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.Shape;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestChip {
public static void main(String[] args) {
new TestChip();
}
public TestChip() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int width = 44;
int height = 17;
Front front = new Front(width, height);
Top top = new Top(width, height);
Side side = new Side(width, height);
draw(g2d, front, Color.BLACK, Color.YELLOW);
draw(g2d, top, Color.BLACK, Color.GRAY);
draw(g2d, side, Color.BLACK, Color.DARK_GRAY);
g2d.dispose();
}
protected void draw(Graphics2D g2d, Shape shape, Color foreground, Color background) {
g2d.setColor(background);
g2d.fill(shape);
g2d.setColor(foreground);
g2d.draw(shape);
}
}
public class Front extends Path2D.Float {
public Front(float width, float height) {
float frontWidth = width * 0.77f;
float yOffset = height * 0.412f;
moveTo(0, yOffset);
lineTo(frontWidth, yOffset);
lineTo(frontWidth, height);
lineTo(0, height);
closePath();
}
}
public class Side extends Path2D.Float {
public Side(float width, float height) {
float xOffset = width * 0.77f;
float inset = height * 0.47f;
moveTo(xOffset, inset);
lineTo(width, 0);
lineTo(width, inset);
lineTo(xOffset, height);
closePath();
}
}
public class Top extends Path2D.Float {
public Top(float width, float height) {
float inset = width * 0.2f;
float shapeHeight = height * 0.412f;
moveTo(inset, 0);
lineTo(width, 0);
lineTo(width - inset, shapeHeight);
lineTo(0, shapeHeight);
closePath();
}
}
}
Your job is to now go away, study the example, study the referenced tutorials, study the associated API docs and figure out how to align the above shape within you circle and draw it's legs...
A hint. Make a "Bug" class that knows how to renderer all this and simply translate the position of the Graphics as required...
I have already made this program able to draw an instance of a small ball bouncing around a screen using these two classes
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import javax.swing.JPanel;
import javax.swing.Timer;
public class move extends JPanel implements ActionListener
{
Timer t = new Timer(7, this);
int x = 10, y = 10, velX = 7, velY = 7;
public void paintComponent(Graphics g, Graphics h)
{
super.paintComponent(h);
super.paintComponent(g);
System.out.println(g);
Graphics2D g2 = (Graphics2D) g;
Ellipse2D circle = new Ellipse2D.Double(x, y, 40, 40);
g2.fill(circle);
t.start();
}
public void actionPerformed(ActionEvent e) {
if(x<0 || x > getWidth())
{
velX = -velX;
}
if(y < 0 || y > getHeight())
{
velY = -velY;
}
x += velX;
y += velY;
repaint();
}
}
This class just simply draws the ball and provides the logic for the timer and such
import java.awt.Color;
import javax.swing.JFrame;
public class Gui {
public static void main(String[] args)
{
move s = new move();
JFrame f = new JFrame("move");
f.add(s);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(1000, 1000);
f.setTitle("Moving Circle");
f.setBackground(Color.GREEN);
}
}
This next class just puts it all in a JFrame, very simple stuff I know but I'm just trying to draw multiple instances within the same JFrame. I'm just trying to experiment with my knowledge of code, some sample of code to implement would be great.
How to draw multiple moving graphics?
This code could have a class Ball class that knows its position & size and how to draw itself to a Graphics.
As each ball is created, they are added to a list. At time of paint, iterate the list and paint each Ball.