I have created a time lapse to show the density on a highway over the course of a day. The data is held in a double[][] array data, where data.length is 2880 (each index represents a 30-second interval) and data[0].length is about 450 (representing a cubic interpolation across the highway section's length).
My code for the time lapse is as follows:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.Timer;
public class TimeLapse extends JPanel {
static double[][] data;
int index = 0;
double max;
double lineWidth;
Timer timer;
final JProgressBar progressBar = new JProgressBar(0, 2879);
BufferedImage[] images;
Color colors[][];
public static void main(String[] args) {
data=getData(); //arbitrary method to get interpolated data
new TimeLapse(data, 90);
}
public TimeLapse(double[][] data1, double max) {
data = data1;
this.max = max;
JFrame frame = new JFrame("Timelapse");
frame.setPreferredSize(new Dimension(800, 600));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.anchor = GridBagConstraints.NORTH;
c.gridwidth = 1;
c.gridx = 0;
c.gridheight = 1;
c.weightx = 1;
c.weighty = .01;
c.gridy = 0;
frame.add(progressBar, c);
c.anchor = GridBagConstraints.SOUTH;
c.gridy = 1;
c.gridheight = 9;
c.weighty = 1;
frame.add(this, c);
frame.pack();
getColorArray();
frame.setVisible(true);
int dataLength;
dataLength = data.length;
// Make the animation 5 seconds long
int delay = (int) (5000d / dataLength);
timer = new Timer(delay, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
updateIndex();
repaint();
Toolkit.getDefaultToolkit().sync();
}
});
timer.start();
}
private void getColorArray() {
double cutOff = max / 2;
colors = new Color[data.length][data[0].length];
for (int index = 0; index < data.length; index++) {
for (double x = 0; x < data[0].length; x++) {
colors[index][(int) x] =
getColor(data[index][(int) x], cutOff);
}
}
}
private void updateIndex() {
index = index < data.length - 1 ? index + 1 : 0;
progressBar.setValue(2879 * index / data.length);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
int panelHeight = getHeight();
lineWidth = ((double) getWidth()) / ((double) (data[0].length));
// guaranteed counter, as doing it in timer's ActionListener could overlap with rendering
for (double x = 0; x < data[0].length; x++) {
g2.setColor(colors[index][(int) x]);
double rectHeight = panelHeight * data[index][(int) x] / max;
g2.fillRect((int) (x * lineWidth),
(int) (panelHeight - rectHeight),
(int) (lineWidth + 1), (int) rectHeight + 1);
}
g2.dispose();
}
private Color getColor(double value, double cutOff) {
int hueR, hueG, hueB = 0;
if (value < cutOff) {
hueG = 255;
hueR = (int) (255 * value / (cutOff));
} else if (max != cutOff) {
hueR = 255;
hueG = (int) (255 - (255 * (value - cutOff) / (max - cutOff)));
} else {
hueR = 255;
hueG = 0;
}
hueR = (hueR < 0) ? 0 : ((hueR > 255) ? 255 : hueR);
hueG = (hueG < 0) ? 0 : ((hueG > 255) ? 255 : hueG);
hueB = (hueB < 0) ? 0 : ((hueB > 255) ? 255 : hueB);
return new Color(hueR, hueG, hueB);
}
}
The animation functions smoothly, but it typically takes a great deal longer than the five seconds I set it to, which I chalk up to the constant coloring and generation of hundreds of lines in the panel.
To verify that I was correct and it was indeed much slower than it should be, I used the Google Chrome widget that appears when you Google "stopwatch" to time it. Doing this I found that when I ran the stopwatch the animation sped up greatly, as well as whenever I moved my mouse over certain elements (hyperlinks, the tabs at the top, and seemingly anything else that gives a visual response to the mouse hovering). This only happens when I move the mouse or am running the stopwatch; keeping the mouse still does not speed it up, and it appears to only have this behavior while hovering over Chrome (i.e. any other application is fine). Can anyone explain this odd behavior?
EDIT: It also happens while reloading a tab, but not after it's done reloading.
EDIT 2: I now know for certain that the timer is speeding up. I created a small class with a timer that prints every millisecond an increasing index:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
public class TimerTest {
static int index = 0;
public static void main(String[] args) {
Timer t = new Timer(1, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(index++);
}
});
t.start();
while (true) {
}
}
}
This has exactly the same behavior as the time lapse class, speeding up greatly when moving the mouse over Chrome's elements. I believe the reason is that, in this case, println is a slow method and executes slower than the timer updates. Again, can someone explain why Chrome specifically speeds up a backed-up timer?
A painting method is for painting only.
You should not be changing properties of your class in the painting method.
That is you can not control when Swing determines a component needs to be repainted. So there may be some system call that is causing the component to be repainted and therefore changing your properties more frequently than you think.
For example you should not be updating your "index" variable or the progress bar value. Instead your Timer should invoke a method to changes these properties and then that method should invoke repaint on the panel.
This only happens when I move the mouse
Maybe you have tooltips on the panel which would cause it to be repainted.
This is easy to test, just add a System.out.println(...) statement to the paintComponent() method to see if it displays more frequently than the 5 seconds of your Timer.
Related
For the purposes of my project, I'm trying to simulate a phyllotaxis pattern by creating multiple circles in real time using the formulas given.
So recently, I've decided to try out GUI programming in Java using JFrame and swing, and I've hit a wall trying to figure out how to get everything running properly. My idea was to slowly print out circle after circle with their x and y coordinates being calculated from the "r = cos/sin(theta)" formulas documented in the phyllotaxis instructions. Unfortunately, while the x and y values are constantly changing, only one circle is printed. Is there something I am missing?
package gExample;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.Timer;
public class GraphicsExample extends Canvas implements ActionListener {
private final static int HEIGHT = 600;
private final static int WIDTH = 600;
private int n = 0;
private int x, y;
Timer t = new Timer(20, this);
public static void main(String args[]) {
JFrame frame = new JFrame();
GraphicsExample canvas = new GraphicsExample();
canvas.setSize(WIDTH, HEIGHT);
frame.add(canvas);
frame.pack();
frame.setVisible(true);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
canvas.setBackground(Color.black);
}
public void paint(Graphics g){
Random rand = new Random();
Color col = new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256));
g.setColor(col);
/*each time paint() is called, I expect a new circle to be printed in the
x and y position that was updated by actionPerformed(), but only one inital circle is created. */
g.fillOval(x, y, 8, 8);
t.start();
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
int c = 9;
double r = c * Math.sqrt(n);
double angle = n * 137.5;
//here, every time the method is called, the x and y values are updated,
//which will be used to fill in a new circle
int x = (int) (r * Math.cos(angle * (Math.PI / 180) )) + (WIDTH / 2);
int y = (int) (r * Math.sin(angle * (Math.PI / 180) )) + (HEIGHT / 2);
//when the program is running, this line of code is executed multiple times.
System.out.println("x: " + x + " y: " + y);
n++;
}
}
i'm trying to write a program with Java what generates images by coloring individual pixels with RGB by adding up the value of every RGB-channel by one until it reaches 255 and then adds one on the next RGB channel.
Here an example:
RED is set to 0
GREEN is set to 0
BLUE is set to 0
RED gets added up by one (RED ++)on the first pixel until it reaches 255.
After RED reaches 255 RED gets set to 0 and GREEN gets added up by one (GREEN ++).
The RED channel gets added up again like in step 1 until 255 and then step 2 follows again.
If GREEN is 255 the same method is used for BLUE means that one gets added to BLUE while GREEN and RED will be set to 0 again. Then step 1 again.
After all channels in the first Pixel are 255 the second pixel gets one up on its RED channel. Then it should begin by step 1 again until the second Pixel has a value of 255 on RED, what will set RED back to 0 and GREEN gets one up on the second pixel and so on and so on....
I'm sorry for my bad english and for my limited Java-knowledge.
This is my whole code so far(Stuff in comments is either not important in the project no more, not ready to use or i don't understand how to use it xD):
import javax.swing.JFrame;
//import javax.swing.JLabel;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
//import java.awt.image.BufferedImage;
public class Quadrat
{
public static void main (String[] args)
{ int x = 100;
int y = 0;
int z = 0;
int Max = 255;
int height = 500;
int width = 500;
if (x < Max){
x ++;
} else {
x = 0;
y ++;
}
if (y > Max){
y = 0;
z ++;
}
if (z > Max){
z = 0;
}
JFrame Bild = new JFrame("Bildergenerator");
Bild.setSize(width,height);
Bild.setVisible(true);
Color Kartoffel = new Color(x, y, z, 255);
BufferedImage Test = new BufferedImage(300, 200,
BufferedImage.TYPE_INT_ARGB);
Graphics graphics = Test.getGraphics();
graphics.setColor(Kartoffel);
graphics.fillRect(100, 100, 100, 100);
graphics.dispose();
Test.setRGB(1, 1, Kartoffel.getRGB());
}
/*System.out.println(x);
System.out.println(y);
System.out.println(z);*/
//img.setRGB(x,y,z);
/* JFrame Test = new JFrame("Jframe1");
JLabel Label = new JLabel("");
Test.add(Label);
Test.setSize(xx,yy);
Test.setVisible(true);
Test.setLocation(x/2-(xx/2), y/2-(yy/2));*/
/* JFrame Hyaeaeae = new JFrame("BurrScurrSWAG");
Hyaeaeae.setSize(1000,1000);
Hyaeaeae.setVisible(true);
Hyaeaeae.getContentPane().setBackground( Color.RED);*/
}
I'll hope you can help me in any way and if so i'm very thankful!
Thank you for your Attention!
Have a nice day! c:
You need to create the timer for this as your code is just changing on the first iteration on startup after this nothing is changed so a timer to make sure increments keep on applied on variables
package javatestapp;
import java.awt.Color;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;
public class JavaTestApp extends TimerTask
{
static int x = 100;
static int y = 0;
static int z = 0;
static int Max = 255;
static int height = 500;
static int width = 500;
static JFrame frame;
public static void main(String[] args)
{
InitFrame();
}
public static void incrementValues()
{
if (x < Max)
{
x ++;
}
else
{
x = 0;
y ++;
}
if (y > Max)
{
y = 0;
z ++;
}
if (z > Max)
{
x = 0;
y = 0;
z = 0;
}
}
public static void genImage()
{
Color color = new Color(x,y,z,255);
frame.getContentPane().setBackground(color);
}
public static void InitFrame()
{
frame = new JFrame("Bildergenerator");
frame.setSize(width,height);
frame.setVisible(true);
Timer timer = new Timer(true);
TimerTask timerTask = new JavaTestApp();
timer.scheduleAtFixedRate(timerTask, 0, 10);
}
#Override
public void run()
{
incrementValues();
genImage();
}
}
I am attempting to overlay the text on a JButton over an ImageIcon that is behind it. However, when the imageIcon is added, the text dissapears. Is there any way to specify the order in which it displays?
Below, i have tried to separately add the images and text to see if that would affect it, but no luck.
Can anyone help me out?
private void initButtons() {
int locationX = 0, locationY = 525;
for (int y = 0; y < 8; y++) {
for (int x = 0; x < 8; x++) {
boardArray[x][y] = new ChessButton();
boardArray[x][y].setSize(75, 75);
boardArray[x][y].setLocation(locationX, locationY);
boardArray[x][y].setXAndY(x, y);
if ((x % 2 == 0 && y % 2 == 1) || (x % 2 == 1 && y % 2 == 0)) {
boardArray[x][y].setColour("white");
boardArray[x][y].setIcon(new ImageIcon("Assets/white_null_null.png"));
} else {
boardArray[x][y].setColour("black");
boardArray[x][y].setIcon(new ImageIcon("Assets/black_null_null.png"));
}
//this adds the images in an alternating pattern
chessFrame.add(boardArray[x][y]);
locationX = locationX + 75;
}
locationX = 0;
locationY = locationY - 75;
}
}
void initPieces() {
for (int y = 0; y < 8; y++) {
for (int x = 0; x < 8; x++) {
if ((x % 2 == 0 && y % 2 == 1) || (x % 2 == 1 && y % 2 == 0)) {
boardArray[x][y].setFont(new Font("Arial Unicode MS", Font.PLAIN, 40));
boardArray[x][y].setText("\u2654");//sets a particular chess piece as text, just testing it now.
} else {
boardArray[x][y].setFont(new Font("Arial Unicode MS", Font.PLAIN, 40));
boardArray[x][y].setText("\u2654");//sets a particular chess piece as text, just testing it now.
//this is suposed to overlay the image over the text, but it is not.
}
}
}
}
First of all, I see you're calling this method: boardArray[x][y].setLocation(locationX, locationY);
.setLocation(...) indicates you're using a null layout, please read Null layout is evil and Why is it frowned upon to use a null layout in Swing? to know why you should avoid its use.
For creating a Chess board, I'd be using GridLayout, please read how to use the different layout managers
Now, to overlay text over the icon, you only need to call JButton#setHorizontalAlignment(...) method and pass SwingConstants.CENTER as the parameter.
For example:
import java.awt.Color;
import java.awt.Font;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
public class ButtonWithImageAndText {
private JFrame frame;
private JButton button;
private Icon icon;
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new ButtonWithImageAndText().createAndShowGui());
}
private void createAndShowGui() {
frame = new JFrame(getClass().getSimpleName());
try {
icon = new ImageIcon(ImageIO.read(getClass().getResource("up.jpg")));
} catch (IOException e) {
e.printStackTrace();
}
button = new JButton();
button.setIcon(icon);
button.setText("Click me!");
button.setHorizontalTextPosition(SwingConstants.CENTER);
button.setFont(new Font("Arial", Font.PLAIN, 40));
button.setForeground(Color.RED);
frame.add(button);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}
Which gives the following output:
For your future questions, please read how to make a valid Minimal, Complete and Verifiable Example (MCVE) that demonstrates your issue and your best try to solve it yourself, the code I posted is a good example of it, as you can copy-paste it and see the same result as I
Use the setComponentZOrder(...) method of the Container class. This will enable you to set the Z-index of the components to set them in what ever order you like. Look also this
I have a Java Swing assignment with the following objectives:
When the program starts, it draws 20 unfilled circles, with radius and location of each determined at random.
If the perimeter line of a circle does NOT intersect any other circle, draw the outline of the circle in RED. If it does intersect at least one other circle, draw it in BLACK.
Add a JButton that, each time it is pressed, creates a new set of circles as described above.
I've completed objectives #1 and #3 above, but I'm stumped on objective #2.
Before I present the code, let me give my understanding of the math behind it. There are two ways a circle can NOT intersect another circle:
The circles are too far apart to share a perimeter point, i.e. the distance between their centers is greater than the sum of their radii (d > r1 + r2). Example.
One circle is completely inside another circle, and their perimeters do not touch, i.e. the distance between their centers is less than the difference between their radii (d < |r1 - r2|). Example.
What I've got so far:
To compare circles, they must be specified before they are drawn, so I used a for-loop to store 20 values in arrays for the center coordinates (int[] x, int[] y) and the radius (double[] radius).
Next, I used nested for-loops to iterate through the array and compare two circles, except when a circle is compared with itself (index j = index k). If the circles intersect, g.setColor(Color.RED). If not, g.setColor(Color.BLACK).
When I execute my code, the circles without any overlap are properly colored red. However, some of the overlapping circles are colored red as well. I assume that they were non-overlapping at the time they were drawn, but were intersected thereafter. How do I fix the code to account for this discrepancy in time? (Problem area located near the bottom, in IntersectingCircles class)
import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.JButton;
public class ButtonFrame extends JFrame
{
private final JButton resetButton = new JButton("Reset");
public ButtonFrame()
{
super("Drawing Random Circles");
setLayout(new BorderLayout());
IntersectingCircles intersectingCircles = new IntersectingCircles();
this.add(intersectingCircles, BorderLayout.CENTER);
this.add(resetButton, BorderLayout.SOUTH);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setSize(1400, 1400);
ButtonHandler handler = new ButtonHandler();
resetButton.addActionListener(handler);
}
private class ButtonHandler implements ActionListener
{
#Override
public void actionPerformed(ActionEvent event)
{
reset();
}
}
public static void main(String[] args)
{
ButtonFrame buttonFrame = new ButtonFrame();
buttonFrame.setVisible(true);
}
public void reset()
{
ButtonFrame buttonFrame = new ButtonFrame();
buttonFrame.setVisible(true);
}
}
class IntersectingCircles extends JPanel
{
private static final JButton resetButton = new JButton("Reset Circles");
private static final JFrame frame = new JFrame("Intersecting Circles");
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
this.setBackground(Color.WHITE);
int[] x = new int[20];
int[] y = new int[20];
int[] diameter = new int[20];
double[] radius = new double[20];
for (int i = 0; i < 20; i++)
{
int xCoord = (int)(Math.random() * 600);
int yCoord = (int)(Math.random() * 600);
int circleSize = (int)(Math.random() * 550);
x[i] = xCoord;
y[i] = yCoord;
diameter[i] = circleSize;
radius[i] = circleSize / 2.0;
}
for (int j = 0; j < 20; j++)
{
for (int k = 0; k < 20; k++)
{
if (k != j)
{
if (((Math.sqrt((x[k] - x[j]) * (x[k] - x[j]) + (y[k] - y[j])
* (y[k] - y[j]))) > (radius[j] + radius[k])) ||
((Math.sqrt((x[k] - x[j]) * (x[k] - x[j]) + (y[k] - y[j])
* (y[k] - y[j]))) < (Math.abs(radius[j] - radius[k]))))
g.setColor(Color.RED);
else
g.setColor(Color.BLACK);
g.drawOval(x[j], y[j], diameter[j], diameter[j]);
}
else
continue;
}
}
}
}
You have logic mistake in if statement inside the cycle - you can set black color then revert to red for some other pair circle. Possible solution draft:
for (int j = 0; j < 20; j++)
{
g.setColor(Color.RED); //set non-intersect state
for (int k = j + 1; k < 20; k++) //avoid excessive work
{
if (intersect test)
{
g.setColor(Color.BLACK);
break; //can stop here
};
g.drawOval(x[j], y[j], diameter[j], diameter[j]);
}
}
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Closed 9 years ago.
Improve this question
I'm trying to use a double that has a very small value to produce one of a more regular size. For example, dividing 1/(double) would produce something like 14.12848572.... I then cast this double into an int to produce a number which I can use to draw an image in a JPanel.
The issue I've been having is that the image does not draw how I expect it to draw. I think this is because there's something I don't understand about how casting doubles works. Anyone who can tell me a bit about how this process actually goes down would be very helpful.
EDIT:
The purpose of this code is translating a monetary value into the size of a bar on a graph. The size of these bars should change as the value changes so that they utilize space in the most efficient manner.
Therefore...
Due to the nature of the monetary values, max will never be less than 430.
I want the image to be bigger the smaller xscale is. If there are fewer values to graph, xscale is smaller, and then the bars are drawn bigger.
EDIT:
The following image shows what my program is currently drawing. What I would like to draw is a series of bars on a bar graph. The xscale and associated variables are what I am primarily concerned with right now.
EDIT:
The SSCCE is completed (I think)! If you run this code, you will see the drawing I don't want. If you change barwidth to equal 7 or some normal int, you will see something that is more along the lines of what I want drawn. Please let me know if there is anything more I should do to make things easier!
EDIT: Copy/pasted wrong code, has been corrected (derp)
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class castingexample extends JPanel
{
private static int ypoint;
private static int barheight;
private static Color color;
private static int bars = 10;
private static int xpoint = 0;
private static int barwidth = 7;
private static double xscale = 7;
private static int yscaleplus = 10000;
private static int yscaleneg = 0;
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.add(new castingexample());
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500,500);
}
public castingexample()
{
new Timer(100, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
resize();
repaint();
}
}).start();
}
public void resize()
{
xscale = bars/(800 - bars*5);
xscale = 1/xscale;
}
public void paintComponent (Graphics g)
{
super.paintComponent(g);
for (int i = 0; i < bars; i++)
{
barheight = 200;
barwidth = (int) (xscale);
ypoint = 450 - barheight;
xpoint = 105+(barwidth + 5)*i;
if(ypoint < 450)
color = Color.green;
else
color = Color.red;
g.setColor(color);
g.fillRect(xpoint, ypoint, barwidth, barheight);
}
}
}
Here's some relevant code:
public void resize()
{
int max = 0;
int min = 0;
for (int i = 0; i < bars; i++)
{
if (getTime(i) > max)
max = (int)getTime(i);
}
yscaleplus = max/430;
/*
for (int i = (int)(getLife() - getRetire() + 1); i < bars; i++)
{
if (getTime(i) < min)
min = (int)getTime(i);
}
yscaleneg = Math.abs(min/200);
*/
xscale = bars/(800 - bars*5);
xscale = 1/xscale;
}
public void paintComponent (Graphics g)
{
super.paintComponent(g);
g.drawLine(100, 20, 100, 630);
g.drawLine(100, 450, 900, 450);
for (int i = 0; i < bars; i++)
{
barheight = (int) (getTime(i)/yscaleplus);
barwidth = (int) (xscale);
ypoint = 450 - barheight;
xpoint = 105+(barwidth + 5)*i;
if(ypoint < 450)
color = Color.green;
else
color = Color.red;
g.setColor(color);
g.fillRect(xpoint, ypoint, barwidth, barheight);
}
}
doubles do not store information as you seem to think, they store information as a value followed by an exponential value of 2^x, this removes the capability you are trying to use
take a look at Math.round(x) instead.