How can I generate a continous graphic in a JPanel with threads? - java

I am trying to generate two continous graphs in a JPanel without blocking the JFrame, with Graphics and Graphics2D library.
Example of one: https://gyazo.com/2027cab6799d8416b1df8ee953d71b21
What I plan to do with this is to generate the graph at the same time that the user can change values and automatically the graph changes and is generated with these.
I am trying using Threads
Thread:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Thread3 extends Thread {
static int state;
double X = 0;
// Workspace state = 1
#Override
public void run() {
if (state == 1) {
// Graphic 2
Graphics g = Thread1.ws.graphic2.getGraphics();
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
double lastX = 3;
double lastY = 0;
double Y = 0;
double XonScreen = 0;
int width = Thread1.ws.graphic2.getWidth();
int height = Thread1.ws.graphic2.getHeight();
while (Thread1.active) {
if (Thread1.ws.graphic2Active) {
try {
Thread.sleep(1);
} catch (InterruptedException ex) {
}
g.clearRect(0, 0, width, height);
XonScreen = 0;
for (int j = 0; j < 48; j++) {
lastX = XonScreen;
lastY = Y;
X += 0.005 * 10000;
XonScreen += 0.005 * 10000;
Y = Math.sin(X) * 120;
g2.drawLine(0, height / 2, width, height / 2);
g2.drawLine((int) XonScreen, ((int) Y + height / 2), (int) lastX, ((int) lastY + height / 2));
g2.drawLine(0, 0, 0, height);
}
} else {
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
Logger.getLogger(Thread3.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
}
}
In this case I just set the JPanel to public and instantiated the JFrame in a static way. So far it works correctly but when running it, the graph does not update, it only updates when there is an event running.
https://gyazo.com/dc72d3c1a119b2ec93b442063f32c797
I tried using frame.repaint(); but it just makes all the JPanel flick. I also tried using a timer with an ActionPerformed event:
private Timer timer = new Timer(20, new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
repaint();
}
});
but it also didn't work (I'm not sure if I implemented it correctly; if you have any suggestions about this, please let me know.) .
Do you know of any way that I can make the graphs update constantly and still manage the rest of the frame without any problems?

Related

Repainting an instance of a class from an ArrayList

Ok so I am very new to Java Swing and a beginner in Java in general. My current problem is I have designed a "cityscape". I am working on a UFO flying around, but my randomly generated buildings continue to get regenerated. I am wondering if there is a way to save my instance of buildings to an ArrayList as I have attempted, and paint that selection from that list each time paint is called. I tried what I thought of and I believe it just crashed it when run, because it didn't even open a JFrame and instead produced errors upon errors. Here is what I have:
CityScape class (the main class):
import java.awt.*;
import javax.swing.*;
public class CityScape extends JPanel
{
Buildings a = new Buildings ();
UFO b = new UFO();
#Override
public void paint (Graphics g)
{
//RememberBuildings.buildingList.get(1).paint(g);
a.paint(g);
b.paint(g);
}
public void move()
{
b.move();
}
public static void main(String[] args) throws InterruptedException
{
JFrame frame = new JFrame("Frame");
CityScape jpe = new CityScape();
frame.add(jpe);
frame.setSize(800, 750);
frame.setBackground(Color.BLACK);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
System.out.println(frame.getContentPane().getSize());
while (true)
{
jpe.move(); //Updates the coordinates
jpe.repaint(); //Calls the paint method
Thread.sleep(10); //Pauses for a moment
}
}
}
Buildings class (the class that generates the buildings):
import java.awt.*;
public class Buildings
{
private int maxX = 784;
private int maxY = 712;
private int width = (int)(Math.random()*100+100);
private int height = (int)(Math.random()*350+100);
private int rows = Math.round((height)/25);
private int columns = Math.round(width/25);
public void addBuilding()
{
RememberBuildings.addBuilding();
}
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
Color transYellow = new Color (255, 255, 0, 59);
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, maxX, maxY);
g2d.setColor(Color.WHITE);
g2d.fillRect(5, 5, 25, 25);
int a = 0;
for (int i =10; i<634; i+=(a+10))//buildings
{
g2d.setColor(Color.GRAY);
g2d.drawRect(i, maxY-height, width, height);
g2d.fillRect(i, maxY-height, width, height);
rows = Math.round((height)/25);
columns = Math.round(width/25);
for (int j = 1; j<=columns; j++)//windows
{
for (int k = 1; k<=rows; k++)
{
g2d.setColor(Color.BLACK);
g2d.drawRect(i+5*j+20*(j-1), (maxY-height)+5*k+20*(k-1), 20, 20);
if (Math.random()<0.7)
{
g2d.setColor(Color.YELLOW);
g2d.fillRect(i+5*j+20*(j-1), (maxY-height)+5*k+20*(k-1), 20, 20);
}
else
{
g2d.setColor(Color.BLACK);
g2d.fillRect(i+5*j+20*(j-1), (maxY-height)+5*k+20*(k-1), 20, 20);
g2d.setColor(transYellow);
g2d.fillRect(i+5*j+20*(j-1), (maxY-height)+5*k+20*(k-1), 20, 20);
}
}
}
addBuilding();
a = width;
height = (int)(Math.random()*462+100);
width = (int)(Math.random()*100+100);
}
}
}
RememberBuildings class (the point of this is to add an instance to an ArrayList):
import java.util.*;
public class RememberBuildings
{
public static ArrayList<Buildings> buildingList = new ArrayList<Buildings>();
public static void addBuilding()
{
buildingList.add(new Buildings());
}
}
And finally my UFO class (creates the UFO flying by):
import java.awt.*;
import javax.swing.*;
public class UFO extends JPanel
{
private int x = 20; //x and y coordinates of the ball
private int y = 20;
private int xa = 1;
public void move() //Increase both the x and y coordinates
{
if (x + xa < 0) {
xa = 1;
}
if (x + xa > 784-75)
{
xa = -1;
}
x = x + xa;
}
public void paint(Graphics g)
{
super.paint(g); //Clears the panel, for a fresh start
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.LIGHT_GRAY);
g2d.fillOval(x,y,75,25); //Draw the ball at the desired point
}
}
Avoid overriding paint, use paintComponent instead. Always call the super paint method before you do any custom painting to ensure that the paint chain is maintained. See Painting in AWT and Swing and Performing Custom Painting for more details
Beware, Swing is not thread safe and it's unwise to update any component (or any variable that a component may rely on) from outside the context of the Event Dispatching Thread. A simple solution might be to use a Swing Timer instead of a while (true) loop and Thread.sleep. See How to use Swing Timers for more details.
You should also only create and modify UI components from within the context of the event dispatching thread, see Initial Threads for more details
If you have a problem with your code not working, you should consider providing a runnable example which demonstrates your problem. This is not a code dump, but an example of what you are doing which highlights the problem you are having. This will result in less confusion and better responses. Providing code which is not runnable and is missing classes makes it difficult to know why it's not working and how to fix it.
A few things here:
To address the paintComponent note and view an example, check out this other thread: Concerns about the function of JPanel: paintcomponent()
There seems to be a bit of a disconnect between the logic you've got going and the object-oriented programming logic that I think will help sort things out (for general info on OOP: https://en.wikipedia.org/wiki/Object-oriented_programming):
What You've Got:
The Structure you've got going is as follows:
CityScape :: here's where you've extended JPanel and setup the main function
UFO :: an object class that represents 1 UFO
Building :: a class that has methods for drawing randomized buildings and calling methods in RememberBuildings
RememberBuildings :: I think this is intended to track buildings that have been drawn
The issue here is that your Building class's paint method continually draws multiple newly randomized buildings instead of a set building that retains its structure.
My Suggestion:
There are plenty of solutions to this issue and different ways to implement each solution, but my recommendation is to remodel your Building class in an OOP fashion, meaning that it would represent 1 single building (truer to the name of the class). This would contain a constructor that initializes all of the randomized dimensions of that single building once and draws that single building on the jpanel. Then you would need to keep an array or list of some sort in the cityscape that contains buildings that are part of the cityscape, eliminating the need for a "RememberBuildings" class. so roughly:
CityScape extends JPanel:
variables:
Building[] buildings; //might be useful to use an arraylist/stack/queue instead of an array depending on implementation
UFO craft;
constructor:
setup new Building objects and add to list buildings
initialize craft to new UFO
paintComponent:
calls the paint methods for each building & the ufo craft
Building:
variables:
int x, y; // position of building
int height, width; // of this building
constructor:
initializes x, y // probably needs to be inputed from CityScape with this setup
calc height and width randomly // stored in this.height/width
paint:
paints single building based on it's variables
//side-note, you'll probably need getters for the x/y/width to build each building from CityScape
Everything else should be much the same.
Good Luck !
So, every time Buildings#paint is called, it regenerates all the builds, which is done randomly.
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
Color transYellow = new Color(255, 255, 0, 59);
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, maxX, maxY);
g2d.setColor(Color.WHITE);
g2d.fillRect(5, 5, 25, 25);
int a = 0;
for (int i = 10; i < 634; i += (a + 10))//buildings
{
g2d.setColor(Color.GRAY);
g2d.drawRect(i, maxY - height, width, height);
g2d.fillRect(i, maxY - height, width, height);
rows = Math.round((height) / 25);
columns = Math.round(width / 25);
for (int j = 1; j <= columns; j++)//windows
{
for (int k = 1; k <= rows; k++) {
g2d.setColor(Color.BLACK);
g2d.drawRect(i + 5 * j + 20 * (j - 1), (maxY - height) + 5 * k + 20 * (k - 1), 20, 20);
if (Math.random() < 0.7) {
g2d.setColor(Color.YELLOW);
g2d.fillRect(i + 5 * j + 20 * (j - 1), (maxY - height) + 5 * k + 20 * (k - 1), 20, 20);
} else {
g2d.setColor(Color.BLACK);
g2d.fillRect(i + 5 * j + 20 * (j - 1), (maxY - height) + 5 * k + 20 * (k - 1), 20, 20);
g2d.setColor(transYellow);
g2d.fillRect(i + 5 * j + 20 * (j - 1), (maxY - height) + 5 * k + 20 * (k - 1), 20, 20);
}
}
}
addBuilding();
a = width;
height = (int) (Math.random() * 462 + 100);
width = (int) (Math.random() * 100 + 100);
}
}
There's two ways you might be able to solve this, which you use will depend on what you want to achieve. You could render the buildings directly to a BufferedImage and simply paint that on each paint cycle or you could cache the information you need in order to re-create the buildings.
The BufferedImage approach is quicker, but can't be animated, so if you want to animate the buildings in some way (make the lights flicker), you will need to build up a series of information which allows you to simply repaint them.
I'm going for the second, as you've asked about painting assets from a ArrayList.
I started by translating your "paint" code into a single concept of a virtual building, which has also has information about it's own lights.
public class Building {
protected static final Color TRANS_YELLOW = new Color(255, 255, 0, 59);
private int x, y, width, height;
private List<Light> lights;
public Building(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
lights = new ArrayList<>(25);
int rows = Math.round((height) / 25);
int columns = Math.round(width / 25);
for (int j = 1; j <= columns; j++)//windows
{
for (int k = 1; k <= rows; k++) {
Color color = null;
if (Math.random() < 0.7) {
color = Color.YELLOW;
} else {
color = TRANS_YELLOW;
}
lights.add(new Light(x + 5 * j + 20 * (j - 1), y + 5 * k + 20 * (k - 1), color));
}
}
}
public void paint(Graphics2D g2d) {
g2d.setColor(Color.GRAY);
g2d.drawRect(x, y, width, height);
g2d.fillRect(x, y, width, height);
for (Light light : lights) {
light.paint(g2d);
}
}
public class Light {
private int x, y;
private Color color;
public Light(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
public void paint(Graphics2D g2d) {
g2d.setColor(Color.BLACK);
g2d.fillRect(x, y, 20, 20);
g2d.setColor(color);
g2d.fillRect(x, y, 20, 20);
}
}
}
This allows you to generate the primary parameters for the Building and simple cache the results and when needed, simply paint it.
For example...
public class Buildings {
private int maxX = 784;
private int maxY = 712;
private List<Building> buildings;
public Buildings() {
buildings = new ArrayList<>(25);
for (int i = 10; i < 634; i += 10)//buildings
{
int width = (int) (Math.random() * 100 + 100);
int height = (int) (Math.random() * 350 + 100);
int x = i;
int y = maxY - height;
buildings.add(new Building(x, y, width, height));
}
}
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
for (Building building : buildings) {
building.paint(g2d);
}
}
}
I also changed your UFO class so it no longer extends from JPanel, as it just doesn't need to and is probably the primary cause of confusion with your painting.
I then updated your paint method in your CityScape to use paintComponent instead...
public class CityScape extends JPanel {
Buildings a = new Buildings();
UFO b = new UFO();
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
a.paint(g);
b.paint(g);
}
As a runnable example...
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class CityScape extends JPanel {
Buildings a = new Buildings();
UFO b = new UFO();
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
a.paint(g);
b.paint(g);
}
public void move() {
b.move();
}
public static void main(String[] args) throws InterruptedException {
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("Frame");
CityScape jpe = new CityScape();
frame.add(jpe);
frame.setSize(800, 750);
frame.setBackground(Color.BLACK);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
System.out.println(frame.getContentPane().getSize());
Timer timer = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
jpe.move(); //Updates the coordinates
jpe.repaint(); //Calls the paint method
}
});
timer.start();
}
});
}
public class Buildings {
private int maxX = 784;
private int maxY = 712;
private List<Building> buildings;
public Buildings() {
buildings = new ArrayList<>(25);
for (int i = 10; i < 634; i += 10)//buildings
{
int width = (int) (Math.random() * 100 + 100);
int height = (int) (Math.random() * 350 + 100);
int x = i;
int y = maxY - height;
buildings.add(new Building(x, y, width, height));
}
}
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
for (Building building : buildings) {
building.paint(g2d);
}
}
}
public static class Building {
protected static final Color TRANS_YELLOW = new Color(255, 255, 0, 59);
private int x, y, width, height;
private List<Light> lights;
public Building(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
lights = new ArrayList<>(25);
int rows = Math.round((height) / 25);
int columns = Math.round(width / 25);
for (int j = 1; j <= columns; j++)//windows
{
for (int k = 1; k <= rows; k++) {
Color color = null;
if (Math.random() < 0.7) {
color = Color.YELLOW;
} else {
color = TRANS_YELLOW;
}
lights.add(new Light(x + 5 * j + 20 * (j - 1), y + 5 * k + 20 * (k - 1), color));
}
}
}
public void paint(Graphics2D g2d) {
g2d.setColor(Color.GRAY);
g2d.drawRect(x, y, width, height);
g2d.fillRect(x, y, width, height);
for (Light light : lights) {
light.paint(g2d);
}
}
public class Light {
private int x, y;
private Color color;
public Light(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
public void paint(Graphics2D g2d) {
g2d.setColor(Color.BLACK);
g2d.fillRect(x, y, 20, 20);
g2d.setColor(color);
g2d.fillRect(x, y, 20, 20);
}
}
}
public class UFO {
private int x = 20; //x and y coordinates of the ball
private int y = 20;
private int xa = 1;
public void move() //Increase both the x and y coordinates
{
if (x + xa < 0) {
xa = 1;
}
if (x + xa > 784 - 75) {
xa = -1;
}
x = x + xa;
}
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.LIGHT_GRAY);
g2d.fillOval(x, y, 75, 25); //Draw the ball at the desired point
}
}
}

How to adjust graphics on Swing progress indicator?

The lower source code, with the pictured example, from the post
Circular Progress Bar for Java Swing not working, is a great Swing feature.
I'd like to be able to use it with a "transparent" JFrame or glass pane
but the graphic "petals", in paint(), want to interact with the background,
so if the opacity of the background is very low, you can barely
see the "petals". Not being familiar with the Graphics2D functions there, I've taken many stabs in the dark to try to adjust the code, but no luck, so could someone who knows how those functions work,
suggest changes so that the "petals" don't interact with the background,
and start out solid white, and gradually fade, as the code does?
I also don't need any fade-in or fade-out delays, and I'm also
having difficulty with that, but if someone could just suggest
modifications for the "petals", that would be great!
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import javax.swing.*;
import javax.swing.plaf.LayerUI;
public class Loading_Test {
static final WaitLayerUI layerUI = new WaitLayerUI();
JFrame frame = new JFrame("JLayer With Animated Gif");
public Loading_Test() {
JPanel panel = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 300);
}
};
JLayer<JPanel> jlayer = new JLayer<>(panel, layerUI);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(jlayer);
frame.pack();
frame.setVisible(true);
layerUI.start();
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Loading_Test loading_Test = new Loading_Test();
}
});
}
}
class WaitLayerUI extends LayerUI<JPanel> implements ActionListener {
private boolean mIsRunning;
private boolean mIsFadingOut;
private Timer mTimer;
private int mAngle;
private int mFadeCount;
private int mFadeLimit = 15;
#Override
public void paint(Graphics g, JComponent c) {
int w = c.getWidth();
int h = c.getHeight();
super.paint(g, c); // Paint the view.
if (!mIsRunning) {
return;
}
Graphics2D g2 = (Graphics2D) g.create();
float fade = (float) mFadeCount / (float) mFadeLimit;
Composite urComposite = g2.getComposite(); // Gray it out.
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .5f * fade));
g2.fillRect(0, 0, w, h);
g2.setComposite(urComposite);
int s = Math.min(w, h) / 5;// Paint the wait indicator.
int cx = w / 2;
int cy = h / 2;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(new BasicStroke(s / 4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2.setPaint(Color.white);
g2.rotate(Math.PI * mAngle / 180, cx, cy);
for (int i = 0; i < 12; i++) {
float scale = (11.0f - (float) i) / 11.0f;
g2.drawLine(cx + s, cy, cx + s * 2, cy);
g2.rotate(-Math.PI / 6, cx, cy);
g2.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, scale * fade));
}
g2.dispose();
}
#Override
public void actionPerformed(ActionEvent e) {
if (mIsRunning) {
firePropertyChange("tick", 0, 1);
mAngle += 3;
if (mAngle >= 360) {
mAngle = 0;
}
if (mIsFadingOut) {
if (--mFadeCount == 0) {
mIsRunning = false;
mTimer.stop();
}
} else if (mFadeCount < mFadeLimit) {
mFadeCount++;
}
}
}
public void start() {
if (mIsRunning) {
return;
}
mIsRunning = true;// Run a thread for animation.
mIsFadingOut = false;
mFadeCount = 0;
int fps = 24;
int tick = 1000 / fps;
mTimer = new Timer(tick, this);
mTimer.start();
}
public void stop() {
mIsFadingOut = true;
}
#Override
public void applyPropertyChange(PropertyChangeEvent pce, JLayer l) {
if ("tick".equals(pce.getPropertyName())) {
l.repaint();
}
}
}
One problem I see is that the code is setting the composite in the wrong place in the loop. It works, but as you've discovered, it's difficult to maintain or change.
g2.setComposite is being called at the end of the loop. This sets the alpha for the next petal drawn. This means there is no easy change you can make to adjust the alpha of the very first petal.
First, I would make the code more in line with the way humans think (at least, the way I think): Set the alpha of the line you're about to draw, right before you draw it:
for (int i = 0; i < 12; i++) {
float scale = (12 - i) / 12f;
g2.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, scale * fade));
g2.drawLine(cx + s, cy, cx + s * 2, cy);
g2.rotate(-Math.PI / 6, cx, cy);
}
Now, making it work with any arbitrary background alpha is easy. We merely adjust the value of scale:
float componentAlpha = 0.5f;
for (int i = 0; i < 12; i++) {
float scale = (12 - i) / 12f;
// Give petals the same relative alpha as the component
// they're overlaying.
scale *= componentAlpha;
g2.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, scale * fade));
g2.drawLine(cx + s, cy, cx + s * 2, cy);
g2.rotate(-Math.PI / 6, cx, cy);
}

Java draw stairs using drawRect and using inputField for more stairs

I'm a beginner whit Java experience so I hope you can help me.
I want to read the number of steps from a JTextfield. But how can you make the stairs variable?
Graphics g = textP.getGraphics();
int x = 5;
int y = 20;
int width = 10;
int height = 10;
For loop 1 - whil give a drawRect of 6 pieces
for (int i = 0; i < 6; i++) {
g.drawRect(x, y * i, width, height);
}
For loop 2 - whil give a drawRect of 5 pieces
for (int i = 1; i < 6; i++) {
g.drawRect(x + 15, y * i, width, height);
}
For loop 3 - whil give a drawRect of 4 pieces
for (int i = 2; i < 6; i++) {
g.drawRect(x + 30, y * i, width, height);
}
For loop 4 - whil give a drawRect of 3 pieces
for (int i = 3; i < 6; i++) {
g.drawRect(x + 45, y * i, width, height);
}
For loop 5 - whil give a drawRect of 2 pieces
for (int i = 4; i < 6; i++) {
g.drawRect(x + 60, y * i, width, height);
}
For loop 6 - whil give a drawRect of 1 pieces
for (int i = 5; i < 6; i++) {
g.drawRect(x + 75, y * i, width, height);
}
Output (must by an rect ipv *):
*
**
***
****
*****
******
You can do it this way.
JTextField stairs = new JTextField("Enter no. of Stairs");
String noOfStairsStr = stairs.getText();
int noOfStairs = Integer.parseInt(noOfStairsStr);
...
for (int i = 0; i < noOfStairs; i++) { // Use that in the for loop.
Are you expecting something like this
for (int i = 0,k=15; i < 6; i++) {
g.drawRect( ( x+(i*k) ), y * i, width, height);
}
A Graphic environment is complex thing. No longer do you have the safety of fixed character width and heights, now you need to start dealing with a wider range of environmental factors (such as the width and height of the area you have to paint in...)
To get start I would suggest you take a look at
Creating a GUI with Swing
Performing custom painting
2D Graphics
To cover the basics.
In order to paint steps, we need to know (at least) three things.
The number of steps to be painted...
The width of each step
The height of each step.
This example calculates the width and height of the steps as a factor of the width and height of the container it's painting on.
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.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class SimpleSteps {
public static void main(String[] args) {
new SimpleSteps();
}
public SimpleSteps() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JTextField steps;
private StairPane stairPane;
public TestPane() {
setLayout(new BorderLayout());
JPanel options = new JPanel();
steps = new JTextField(10);
JButton create = new JButton("Create");
stairPane = new StairPane();
options.add(new JLabel("Step count: "));
options.add(steps);
options.add(create);
add(stairPane);
add(options, BorderLayout.SOUTH);
create.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String value = steps.getText();
try {
int stepCount = Integer.parseInt(value);
stairPane.setStepCount(stepCount);
} catch (NumberFormatException exp) {
JOptionPane.showMessageDialog(TestPane.this, value + " is not a valid step count", "Error", JOptionPane.ERROR_MESSAGE);
}
}
});
}
}
public class StairPane extends JPanel {
private int stepCount = 0;
public StairPane() {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (stepCount > 0) {
Graphics g2d = (Graphics2D) g.create();
int stepHeight = getHeight() / stepCount;
int stepWidth = getWidth() / stepCount;
for (int step = stepCount; step != 0; step--) {
int width = stepWidth * step;
int y = (stepHeight * step) - stepHeight;
g2d.fillRect(0, y, width, stepHeight);
}
g2d.dispose();
}
}
private void setStepCount(int stepCount) {
this.stepCount = stepCount;
repaint();
}
}
}

GUI freezes when drawing Wave from animation on JPanel though i used Swing Timer

plese look at my code snippets , wha is wrong with it , it frrezes GUI when the Swing timer stats which is repeteadly paints on the jpnael ??
class WaveformPanel extends JPanel {
Timer graphTimer = null;
AudioInfo helper = null;
WaveformPanel() {
setPreferredSize(new Dimension(200, 80));
setBorder(BorderFactory.createLineBorder(Color.BLACK));
graphTimer = new Timer(15, new TimerDrawing());
}
/**
*
*/
private static final long serialVersionUID = 969991141812736791L;
protected final Color BACKGROUND_COLOR = Color.white;
protected final Color REFERENCE_LINE_COLOR = Color.black;
protected final Color WAVEFORM_COLOR = Color.red;
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int lineHeight = getHeight() / 2;
g.setColor(REFERENCE_LINE_COLOR);
g.drawLine(0, lineHeight, (int) getWidth(), lineHeight);
if (helper == null) {
return;
}
drawWaveform(g, helper.getAudio(0));
}
protected void drawWaveform(Graphics g, int[] samples) {
if (samples == null) {
return;
}
int oldX = 0;
int oldY = (int) (getHeight() / 2);
int xIndex = 0;
int increment = helper.getIncrement(helper
.getXScaleFactor(getWidth()));
g.setColor(WAVEFORM_COLOR);
int t = 0;
for (t = 0; t < increment; t += increment) {
g.drawLine(oldX, oldY, xIndex, oldY);
xIndex++;
oldX = xIndex;
}
for (; t < samples.length; t += increment) {
double scaleFactor = helper.getYScaleFactor(getHeight());
double scaledSample = samples[t] * scaleFactor;
int y = (int) ((getHeight() / 2) - (scaledSample));
g.drawLine(oldX, oldY, xIndex, y);
xIndex++;
oldX = xIndex;
oldY = y;
}
}
public void setAnimation(boolean turnon) {
if (turnon) {
graphTimer.start();
} else {
graphTimer.stop();
}
}
class TimerDrawing implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
byte[] bytes = captureThread.getTempBuffer();
if (helper != null) {
helper.setBytes(bytes);
} else {
helper = new AudioInfo(bytes);
}
repaint();
}
}
}
I am calling setAnimation of WaveFormPanel from its parent class.when animation starts it does not draw anything but freezes. please , give me solution.
Thank You
Mihir Parekh
The java.swingx.Timer calls the ActionPerformed within the EDT. The question then is, what's taking the time to render. It could be the call to captureThread.getTempBuffer it could be the construction of the help, but I suspect it's just the share amount of data you are trying to paint.
Having playing with this recently, it takes quite a bit of time to process the waveform.
One suggestion might be to reduce the number of samples that you paint. Rather then painting each one, maybe paint every second or forth sample point depending on the width of the component. You should still get the same jest but without all the work...
UPDATED
All samples, 2.18 seconds
Every 4th sample, 0.711 seconds
Every 8th sample, 0.450 seconds
Rather then paint in response to the timer, maybe you need to paint in response to batches of data.
As your loader thread has a "chunk" of data, may be paint it then.
As HoverCraftFullOfEels suggested, you could paint this to a BufferedImage first and then paint that to the screen...
SwingWorker might be able to achieve this for you
UPDATED
This is the code I use to paint the above samples.
// Samples is a 2D int array (int[][]), where the first index is the channel, the second is the sample for that channel
if (samples != null) {
Graphics2D g2d = (Graphics2D) g;
int length = samples[0].length;
int width = getWidth() - 1;
int height = getHeight() - 1;
int oldX = 0;
int oldY = height / 2;
int frame = 0;
// min, max is the min/max range of the samples, ie the highest and lowest samples
int range = max + (min * -2);
float scale = (float) height / (float) range;
int minY = Math.round(((height / 2) + (min * scale)));
int maxY = Math.round(((height / 2) + (max * scale)));
LinearGradientPaint lgp = new LinearGradientPaint(
new Point2D.Float(0, minY),
new Point2D.Float(0, maxY),
new float[]{0f, 0.5f, 1f},
new Color[]{Color.BLUE, Color.RED, Color.BLUE});
g2d.setPaint(lgp);
for (int sample : samples[0]) {
if (sample % 64 == 0) {
int x = Math.round(((float) frame / (float) length) * width);
int y = Math.round((height / 2) + (sample * scale));
g2d.drawLine(oldX, oldY, x, y);
oldX = x;
oldY = y;
}
frame++;
}
}
I use an AudioStream stream to load a Wav file an produce the 2D samples.
I'm guessing that your wave drawing code, which is being called from within a paintComponent(...) method is taking longer than you think and is tying up both Swing painting and the EDT.
If this were my code, I'd consider drawing my waves to BufferedImages once, making ImageIcons from these images and then simply swapping icons in my Swing Timer.

How come this AffineTransform rotation method works?

Ive been programming a game just to become better at java. I had been having alot of trouble with getting the player rotation to work correctly. My first method used this
g2.setTransform(AffineTransform.getRotateInstance(radAngle,x_pos + (img.getWidth() / 2),y_pos+(img.getHeight() / 2)));
However this caused all images to rotate with the player thus making shooting and aiming completely disfunctional.
i was researching and saw someone use this code to make they're player rotate.
Graphics2D g2 = (Graphics2D)g;
AffineTransform oldTransform = g2.getTransform();
AffineTransform newOne = (AffineTransform)(oldTransform.clone());
newOne.rotate(radAngle,x_pos + (img.getWidth() / 2),y_pos+ (img.getHeight() / 2));
g2.setTransform(newOne);
g2.drawImage(img, x_pos,y_pos,this);
repaint();
g2.setTransform(oldTransform);
This works great and i dont have the same problems i had before. However i dont know why.
Heres my full code. The code above is for the body of the paint method.
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.lang.Math.*;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import javax.imageio.ImageIO;
import java.io.*;
import java.net.URL;
import java.util.ArrayList;
public class Game extends Applet implements Runnable, KeyListener, MouseMotionListener, MouseListener
{
//pos variables keep track of the current position of the player
int x_pos = 250;
int y_pos = 250;
//speed variables keep track of the speed/how many pixels will be added to position during this iteration of the thread
float x_speed = 0;
float y_speed = 0;
int radius = 25;
//denotes the boundries of the applet
int appletsize_x = 800;
int appletsize_y = 600;
//the x and y variables mark whether a movement key is being pressed thats telling the object to move on
//on of those axes's
int x = 0;
int y = 0;
//variables that will indicate whether one of those keys are being depressed
int up = 0;
int down= 0;
int left = 0;
int right= 0;
int mouse_x;
int mouse_y;
int tracking_angle;
//getting some images.
private BufferedImage dbImage;
private BufferedImage test;
private Graphics dbg;
private Image curser;
BufferedImage img = null;
BufferedImage round = null;
double x_dist;
double y_dist;
//i dont use this AffineTransform, although ill leave it here just incase i decide to use it if i continue working
//on this independently.
AffineTransform at = new AffineTransform();
//the angle of the mouse to the player object.
double radAngle;
public void init()
{
try {
URL url = new URL(getCodeBase(), "UFO.png");
img = ImageIO.read(url);
} catch (IOException e) {System.out.println("Cant find player image");
}
try {
URL url = new URL(getCodeBase(), "round.png");
round = ImageIO.read(url);}
catch (IOException e) {System.out.println("round not loading");}
setBackground (Color.black);
setFocusable(true);
addKeyListener( this );
curser = getImage(getDocumentBase(), "mouse.png");
addMouseMotionListener(this);
addMouseListener(this);
try
//changing the curser to the crosshair image
{
Toolkit tk = Toolkit.getDefaultToolkit();
Cursor c = tk.createCustomCursor( curser,new Point( 5, 5 ), "Cross_Hair" );
setCursor( c );
}
catch( IndexOutOfBoundsException x )
{System.out.println("Cross_hair");}
}
public class Shot {
final double angle = radAngle;
double x_loc;
double y_loc;
double X;
double Y;
public Shot(){
x_loc += x_pos;
y_loc += y_pos;
X=Math.cos(radAngle)*5;
Y=Math.sin(radAngle)*5;
}
public void move(){
x_loc += X;
y_loc += Y;}
}
//start the thread
public void start ()
{
Thread th = new Thread (this);
th.start ();
}
public void stop()
{
}
public void destroy()
{
}
//cathces the mouseEvent when the mosue is moved.
public void mouseClicked(MouseEvent e){}
public void mousePressed(MouseEvent e){
Shot shoot = new Shot();
shots.add(shoot);}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
public void mouseReleased(MouseEvent e){}
public void mouseMoved(MouseEvent e){
//get position of mouse
mouse_x = e.getX();
mouse_y = e.getY();
//get the distence from the player to the
//i calculate the actual angle of the mosue from the player object in radians
//this exists more just for debugging purposes since radians make no sense to me
tracking_angle = 90;
}
public void mouseDragged(MouseEvent e){
mouse_x = e.getX();
mouse_y = e.getY();
Shot shoot = new Shot();
shots.add(shoot);}
//this method sets the key variables to zero when the keys are released
public void keyReleased(KeyEvent r)
{
//Right
if (r.getKeyCode() == 68 ){
x = 0;
left = 0;
}
//Left
if (r.getKeyCode() == 65){
x = 0;
right = 0;
}
//Up
if (r.getKeyCode() == 87 ) {
//y_speed = 0;
down = 0;}
//Down
if (r.getKeyCode() == 83 ) {
//y_speed = 0;
up = 0;}
//move();
}
public void keyTyped(KeyEvent t){}
//changes the variables when a key is pressed so that the player object will move
public void keyPressed(KeyEvent r){
//right
if (r.getKeyCode() == 68 ){
left = 1;
}
//left
if (r.getKeyCode() == 65){
right = 1;}
//Down
if (r.getKeyCode() == 87 ) {
down = 1;}
//Up
if (r.getKeyCode() == 83) {
up = 1;}
//move();
}
//sorta like the body of the thread i think
public void run ()
{
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
while (true)
{
System.out.println(Math.tan(radAngle)/1);
x_dist = mouse_x - x_pos;
y_dist = mouse_y - y_pos;
radAngle = Math.atan2(y_dist , x_dist);
//if(tracking_angle < 0){
//tracking_angle = absT
if (left == 1 && x_speed < 11){
x = 0;
x_speed += 1;
}
//Right
if (right == 1 && x_speed > -11){
x = 0;
x_speed -= 1;
}
//Down
if (down == 1 && y_speed > -11) {
y_speed -= 1;}
//Up
if (up == 1 && y_speed < 11) {
y_speed += 1;}
if( x == 0 && x_speed > 0){
x_speed -=.2;}
if( x == 0 && x_speed < 0){
x_speed +=.2;}
if( y == 0 && y_speed > 0){
y_speed -=.2;}
if( y == 0 && y_speed < 0){
y_speed +=.2;}
if (x_pos > appletsize_x - radius && x_speed > 0)
{
x_pos = radius;
}
else if (x_pos < radius && x_speed < 0)
{
x_pos = appletsize_x + radius ;
}
if (y_pos > appletsize_y - radius && y_speed > 0){
y_speed = 0;}
else if ( y_pos < radius && y_speed < 0 ){
y_speed = 0;}
x_pos += (int)x_speed;
y_pos += (int)y_speed;
repaint();
try
{
//tells the thread to wait 15 milliseconds util it executes again.
Thread.sleep (15);
}
catch (InterruptedException ex)
{
}
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
}
}
public void update (Graphics g)
{
if (dbImage == null)
{
dbImage = new BufferedImage(this.getSize().width, this.getSize().height, BufferedImage.TYPE_INT_RGB);
dbg = dbImage.getGraphics ();
}
dbg.setColor (getBackground ());
dbg.fillRect (0, 0, this.getSize().width, this.getSize().height);
dbg.setColor (getForeground());
paint (dbg);
shot_draw(dbg);
g.drawImage (dbImage, 0, 0, this);
}
ArrayList<Shot> shots = new ArrayList<Shot>();
double last_angle = 1000;
public void paint (Graphics g){
Graphics2D g2 = (Graphics2D)g;
AffineTransform oldTransform = g2.getTransform();
AffineTransform newOne = (AffineTransform)(oldTransform.clone());
newOne.rotate(radAngle,x_pos + (img.getWidth() / 2),y_pos+(img.getHeight() / 2));
g2.setTransform(newOne);
g2.drawImage(img, x_pos,y_pos,this);
repaint();
g2.setTransform(oldTransform);
// g2.setTransform(AffineTransform.getRotateInstance(radAngle,x_pos + (img.getWidth() / 2),y_pos+(img.getHeight() / 2)));
//g2.getTransform().setToIdentity();
}
public void shot_draw(Graphics g){
Graphics2D g2 = (Graphics2D)g;
// Shot shoot = new Shot();
// shots.add(shoot);
for(Shot i: shots){
g2.drawImage(round,(int)i.x_loc+40,(int)i.y_loc+40,this);
i.move();}
}}
Here are the images I'm using:
This makes sense since if you don't reset the Graphics object's AffineTransform back to its baseline, it will use the new transform to draw everything including all images. I don't understand however why you have a call to repaint() from within your paint method. You shouldn't do this.
The AffineTransform object is connected to the Graphics2D object via the call to setTransform. Once connected, the transform causes every object drawn using the Graphics2D object to be drawn with that same transform (in this case, rotation) applied to it until a new AffineTransform is assigned to the Graphics2D object via another call to setTransform. The sample code you found saved the old transform (which presumably encodes a normal, non-rotated state) via
AffineTransform oldTransform = g2.getTransform();
The code then created the rotation transform and connected it to the Graphics2D (now all objects drawn would be rotated until a new transform be assigned), then drew the one object that needed to be drawn rotated (which therefore had the newly-created rotation transform applied to it), and then restored the original, non-rotating transform to the Graphics2D object via:
g2.setTransform(oldTransform);
That way, the transform that would be applied to subsequent objects would be the original, non-rotating transform.

Categories