So I found Conway's Game of Life recently, so I'm naturally addicted. It hasn't taken me long to find out that I am very limited by my computer's CPU. I've also found that, for whatever reason, I cannot add a JPanel with many JComponents to a JFrame.
So I have a loop that adds 86,400 JLabels to a JPanel, which happens in ~1 second, but adding this JPanel to a JFrame takes ~2 minutes.
I understand I could use java.awt.Graphics, but I'd prefer to use JLabels because they automatically resize.
So my question: Why does this take so long to add the JPanel to the JFrame, and how do I fix it?
Using java.awt.Graphics, I was able to remove this long period of lag:
public void render(int[][] cells) {
int cellHeight = image.getHeight() / cells.length;
int cellWidth = image.getWidth() / cells[0].length;
for (int y = 0; y < cells.length; y++) {
for (int x = 0; x < cells[y].length; x++) {
int col = colors[cells[y][x]].getRGB();
fillSquare(x * (cellWidth), y * (cellHeight), cellWidth, cellHeight, col);
}
}
}
// Could pass a java.awt.Rectangle here
private void fillSquare(int xPos, int yPos, int width, int height, int col) {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
pixels[(x + xPos) + (y + yPos) * image.getWidth()] = col;
}
}
}
#Override
public void paint(Graphics g) {
g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
}
For this method, it's important to keep the JFrame's size proportional to the number of cells, this way there is no unused space within the JFrame.
Related
I'm having some problems whit drawing a sierpinski carpet, and would apreciate any help.
I was able to define the stoping condition, draw the central rectangle, and recursively, draw the next level of the image, all while keeping count.
It just so happens that I can only draw on the top left side. I'd say I'm confusing variables, but I can't seem to figure it out. Would apreciate any help
This is the part of the code where i'm having problems.
int smallerWidth = newWidth / 3;
int smallerHeight = newHeight / 3;
int sX = 0;
int sY = 0;
if (currentDeep > 1) {
for (int i = 0; i < 3; i++) {
sX = width / 9 + (i * 3 * (width / 9));
sY = height / 9;
g.fillRect(sX, sY, smallerWidth, smallerHeight);
for (int j = 0; j < 3; j++) {
sY = height / 9 + (j * 3 * (height / 9));
g.fillRect(sX, sY, smallerWidth, smallerHeight);
}
}
return 1 + printSquares(g, sX, sY, newWidth, newHeight, currentDeep
- 1);
} else
return 1;
}
This is the full code
https://pastebin.com/WPJ5tG8w
In sum my question is. What should I change/create in order for my program to draw the remaining 7 squares?
The issue with your code is, that you are trying to perform actions for multiple layers of the recursion at once. Normally, in the recursion, you would only paint the Quadrado central, calculate the sizes and coordinates of the smaller rectangles, and call the method recursively. That way you ensure that the recursive calls do not influence the stuff that is already there.
private int printSquares(Graphics g, int xi, int yi, int width, int height, int currentDeep) {
//Quadrado central
int newWidth = width / 3;
int newHeight = height / 3;
int x = (width / 3) + xi;
int y = (height / 3) + yi;
g.fillRect(x, y, newWidth, newHeight);
int sX = 0;
int sY = 0;
if (currentDeep > 1) {
int sum = 0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
//This is the position of each of the small rectangles
sX = i * (width / 3) + xi;
sY = j * (height / 3) + yi;
// Call the method recursively in order to draw the smaller rectangles
sum += printSquares(g, sX, sY, newWidth, newHeight, currentDeep - 1);
}
}
return 1 + sum;
} else
return 1;
}
I hope, this resolves you issue.
I hope this is ok. This is amazing code. I took the liberty to complete this with the original code provided in the question and added the code that fixed it that Illedhar recommended as a added method. Here it is. Thank you for sharing this.
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
public class sierpinskicarpet {
public static Color BACKGROUNDCOLOR = new Color(0, 0, 150);
public static Color FOREGROUNDCOLOR = new Color(255, 180, 0);
// Padrao = 5, alterado
public static int DEEP = 10;
/**
* Build the frame and shows it
*/
public sierpinskicarpet(int deep) {
// the frame and title
JFrame frame = new JFrame();
frame.setTitle("...: Recursive Squares with deep " + deep + " :...");
// Dispose frame on click on close button
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
// set size and center frame on screen
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
// add print area occupying all the frame content area
frame.add(new PrintArea(deep));
// put frame visible
frame.setVisible(true);
}
/**
* Main method
*/
public static void main(String[] args)
{
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
// launch for 1 to DEEP squares frames
for (int i = DEEP; i >= 1; --i) {
// build a new object each time: objects will run
// independently
new sierpinskicarpet(i);
}
}
});
}
}
/**
* Our print area is, in fact, a label extended with the paint squares behavior
*/
class PrintArea extends JLabel {
private static final long serialVersionUID = 1L;
// local deep variable, will keep the registered deep for this the print
// area
int deep;
/**
* constructor
*/
public PrintArea(int deep) {
// call super, that is JLabel, constructor
super();
// set background color and set as well opaque to allow the background
// to be visible
setBackground(sierpinskicarpet.BACKGROUNDCOLOR);
setOpaque(true);
// save the deep
this.deep = deep;
}
/**
* paint method, called by JVM, when it is needed to update the PrintArea
*/
public void paint(Graphics g) {
// call paint from the JLABEL, draws the background of the PrintArea
super.paint(g);
// set drawing color
g.setColor(sierpinskicarpet.FOREGROUNDCOLOR);
// call the amazing print square method
int n = printSquares(g, 0, 0, getWidth(), getHeight(), this.deep);
// put to the world how much squares we printed
System.out.println("Deep = " + deep + ", squares painted: " + n);
}
/**
* Auxiliary method that will to the work. It must print a square with 1/3
* of the length of the frame and at the center and if not the bottom level
* ask to do the same for each of the other 8 square with 1/3 of length but
* called with the new deep
*/
private int printSquares(Graphics g, int xi, int yi, int width, int height, int currentDeep) {
//Quadrado central
int newWidth = width / 3;
int newHeight = height / 3;
int x = (width / 3) + xi;
int y = (height / 3) + yi;
g.fillRect(x, y, newWidth, newHeight);
int sX = 0;
int sY = 0;
if (currentDeep > 1) {
int sum = 0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
//This is the position of each of the small rectangles
sX = i * (width / 3) + xi;
sY = j * (height / 3) + yi;
// Call the method recursively in order to draw the smaller rectangles
sum += printSquares(g, sX, sY, newWidth, newHeight, currentDeep - 1);
}
}
return 1 + sum;
} else
return 1;
}
}
/*
Works Cited:
Recursive changing variables - sierpinski carpet. Stack Overflow. Retrieved May 4, 2022,
from https://stackoverflow.com/questions/49945862/recursive-changing-variables-sierpinski-carpet
*/
I'm new to java and I am trying to make a row of squares using Java that I can use later as a border.
When I run the program the squares appear but either move across the window or disappear.
This is the code below:
public int tileSize = 25;
public int row;
public int x = 0, y = 0;
public void draw(Graphics g){
`g.setColor(new Color(38, 127, 0));`
g.fillRect(0, 0, 650, 550);
g.setColor(Color.black);
for(int row = 0; row <= 650; row++) {
g.drawRect(x, y, tileSize, tileSize);
row = row + 25;
x = x + 25;
y = y + 0;
}
}
There are multiple things wrong with your code.
First of all
When you iterate through your for loop, row get incremented at two different places: at row = row + 25; and when the for loops enters its next iteration (by i++). That way your row isn't 651 - because you used <= instead of <. I suggest you just get rid of row = row + 25;
Second
You declare your x globally and only increment it. That way your row gets shifted each time your Frame gets redrawn. You could declare your x locally in the for-loop, or you could set x back to zero after or before the for-loop.
So a solution could be:
public int tileSize = 25;
public int y = 0; // y doesn't gets changed
public int x = 0;
g.setColor(Color.BLACK);
for(int row = 0; row < 650; row++) {
g.drawRect(x, y, tileSize, tileSize);
x += 25;
}
x = 0;
How does one go about coloring an individual rectangle when it's hovered over? The specific method used below really doesn't give me any ideas on how to solve this problem. It generates a grid in the window using individual rectangles. How would it be possible to listen for mouseX and mouseY and color one rectangle without disrupting this code? Thanks.
int cols,rows;
int scl = 20;
int gridsize = 0;
void setup(){
size(400,400);
int w = 400;
int h = 400;
cols = w / scl;
rows = h / scl;
}
void draw() {
//mouseX, mouseY
background(r,g,b);
for (int x = 0; x < cols; x++){
for (int y = 0; y < rows; y++){
stroke(55);
//noFill();
fill(50,50,50);
rect(x*scl,y*scl,scl,scl);
}
}
}
For reference, I am using Processing 3 for Java.
You can always check if the mouse is within the bounds of a rectangle:
you know the mouseX,mouseY values
you know the x,y and size of each box
if mouseX is within x and x+size and mouseY is within y and y+size you're over a box.
Here's the above applied to your code (if condition formatting for easy visibility, feel free to re-format):
int cols, rows;
int scl = 20;
int gridsize = 0;
void setup() {
size(400, 400);
int w = 400;
int h = 400;
cols = w / scl;
rows = h / scl;
}
void draw() {
//mouseX, mouseY
background(255);
for (int x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) {
int xpos = x*scl;
int ypos = y*scl;
stroke(55);
if(
(mouseX >= xpos && mouseX <= xpos+scl) &&
(mouseY >= ypos && mouseY <= ypos+scl)
){
fill(90);
}else{
fill(50);
}
rect(xpos, ypos, scl, scl);
}
}
}
For more info also checkout the Processing Button example
George's answer works nicely for this scenario, but there is another, a little more complex way too if might want to go Object Oriented here. For this little example, you could have a Grid class which holds and manages an array of Cell objects. Or you can just skip the Grid class and manage the Cells in your main sketch. You could give the Cell class a function to render itself and you could give each cell a color and a size too, it's totally up to you. Also, it could have a function which tells you if your mouse is over it and a function to change its color. A skeleton would look like this:
class Cell {
float x,y;
float length, breadth;
color col;
Cell(float x, float y) {
this.x = x;
this.y = y;
length = 10;
breadth = 10;
col = color(0);
}
void render() {
fill(col);
rect(x, y, length, breadth);
}
void setColor(color col) {
this.col = col;
}
boolean mouseOver() {
if(mouseX > x && mouseX < x+length) {
if(mouseY > y && mouseY < y+breadth) {
return true;
}
}
return false;
}
Now you could just use this class and its methods in your main sketch to find the cell with the mouse over it and call setColor on it to change its color.
George's answer is correct. I upvoted it and I believe you should mark it as the correct answer. Yushi's answer is basically just George's answer, moved into a class.
They both use point-rectangle collision detection, which checks whether the point is inside the rectangle. You just check each rectangle against the point (in your case the mouse position), and that allows you to determine which rectangle the mouse is in. This will work even if you have a bunch of rectangles of different shapes, and will even work with overlapping rectangles.
The other way to do it is using grid-based collision detection which takes advantage of the fact that you have a bunch of evenly spaced rectangles that don't overlap. You'd just use division to figure out which cell the mouse was in, and then you'd convert that cell to coordinates, and you'd use those coordinates to draw the rectangle. That might sound confusing, but it looks like this:
int cols;
int rows;
int scl = 20;
void setup() {
size(400, 400);
cols = width / scl;
rows = height / scl;
}
void draw() {
background(100);
for (int x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) {
stroke(55);
fill(50, 50, 50);
rect(x*scl, y*scl, scl, scl);
}
}
int hoveredRectColX = int(mouseX / scl);
int hoveredRectRowY = int(mouseY / scl);
float rectX = hoveredRectColX * scl;
float rectY = hoveredRectRowY * scl;
fill(255, 0, 0);
rect(rectX, rectY, scl, scl);
}
The last block of code is the meat and potatoes. First it figures out which row and column the mouse is in, then figures out the position of that cell, and uses that to draw a rectangle. If this doesn't make sense, the best thing you can do is get out a piece of paper and a pencil and draw a bunch of examples to see the pattern of what's going on.
Shameless self-promotion: I wrote a tutorial on collision detection in Processing, including both point-rectangle and grid-based collision detection, available here.
I am new with programming. I am not sure how to put an object in the center of a frame. This is how far i got:
public class LetSee extends JPanel {
public void paintComponent(Graphics g) {
int row; // Row number, from 0 to 7
int col; // Column number, from 0 to 7
int x,y; // Top-left corner of square
for ( row = 0; row < 5; row++ ) {
for ( col = 0; col < 5; col++) {
x = col * 60;
y = row * 60;
if ( (row % 2) == (col % 2) )
g.drawRect(x, y, 60, 60);
else
g.drawRect(x, y, 60, 60);
}
} // end for row
}
}
public class LetSeeFrame extends JFrame {
public LetSeeFrame(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(1900, 1000);
setVisible(true);
LetSee let = new LetSee();
let.setLayout(new BorderLayout());
add(let,BorderLayout.CENTER);
setLocationRelativeTo(null);
}
public static void main(String[] args) {
LetSeeFrame l = new LetSeeFrame();
}
}
Actually your panel is centered in the frame, but what it draws isn't.
You should make use of the width and the height of the JPanel to center the drawing .
Also put your sizes and numbers into variables, it is less error-prone when you use them several times in your code .
Finally as #MadProgrammer said in the comments :
Don't forget to call super.paintComponent before you doing any custom
painting, strange things will start doing wrong if you don't. Also
paintComponent doesn't need to be public, no one should ever call it
directly
import java.awt.Graphics;
import javax.swing.JPanel;
public class LetSee extends JPanel {
public void paintComponent(final Graphics g) {
super.paintComponent(g);
int row; // Row number, from 0 to 7
int col; // Column number, from 0 to 7
int x, y; // Top-left corner of square
int maxRows = 5;
int maxCols = 5;
int rectWidth = 60;
int rectHeight = 60;
int maxDrawWidth = rectWidth * maxCols;
int maxDrawHeight = rectHeight * maxRows;
// this is for centering :
int baseX = (getWidth() - maxDrawWidth) / 2;
int baseY = (getHeight() - maxDrawHeight) / 2;
for (row = 0; row < maxRows; row++) {
for (col = 0; col < maxCols; col++) {
x = col * rectWidth;
y = row * rectHeight;
if ((row % 2) == (col % 2)) {
g.drawRect(baseX + x, baseY + y, rectWidth, rectHeight);
} else {
g.drawRect(baseX + x, baseY + y, rectWidth, rectHeight);
}
}
} // end for row
}
}
I am trying to make it so that the position of a pixel in an image (int i, int j) determines the color of that pixel. This is for an explosion effect in a java2d game I want to be extra cool by making the colors of the explosion depend on the position of the explosion. What I am doing currently is creating an ArrayList of colors then using i*j as index, testing this out on a 1000x1000 image shows a mirroring along the diagonal, naturally because i*j = j*i around the diagonal as show below.
Knowing that i=0, j=999 is the 1000th pixel while i=999, j=0 is the 999001th pixel how would you get a mapping f(i,j) != f(j,i) of pixels to colors without first storing the colors in a list? The color ordering is very important, that is to say colors are constructed using R,0,0 then 0,G,0 then 0,0,B
Question wasn't clear apparently.
Notice getAllColors, it creates the colors in order and adds them to the list, notice g2d.setColor(i*j), it sets the color in order except it mirrors along the diagonal. I want to know if I can map the colors to an index(in order) without storing it in a list while avoiding mirroring along the diagonal.
Full MCVE
public class AllColors extends JPanel {
private int width, height;
private double colorIncrement;
private List<Color> colors;
public AllColors(int width, int height) {
this.width = width;
this.height = height;
this.colorIncrement = 1.0 / Math.pow(1.0 * width * height, 1.0 / 3);
this.colors = new ArrayList<>(width * height);
getAllColors();
}
#Override
#Transient
public Color getBackground() {
return Color.black;
}
#Override
#Transient
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
// Notice i*j= j*i around diagonal, the problem
g2d.setColor(colors.get(i * j));
g2d.fillRect(i, j, 1, 1);
}
}
}
private void getAllColors() {
for (float R = 0; R < 1.0; R += colorIncrement)
for (float G = 0; G < 1.0; G += colorIncrement)
for (float B = 0; B < 1.0; B += colorIncrement)
colors.add(new Color(R, G, B));
}
public static void main(String[] args) {
JFrame frame = new JFrame();
AllColors allColors = new AllColors(800, 800);
frame.getContentPane().add(allColors);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
}
}
In the double loop check when i=j then skip the payload.
Knowing that i=0, j=999 is the 1000th pixel while i=999, j=0 is the 999001th pixel how would you get a mapping f(i,j) != f(j,i) of pixels to colors without first storing the colors in a list?
pixel = i * 1000 + j + 1;
As far as storing them in a list is concerned, that may be your best approach, since precalculation can often make things faster. Though I would probably do a two dimensional array. Like:
private void getAllColors() {
colors = new Color[1000][1000];
int i = 0; int j = 0;
loop:
for (float R = 0; R < 1.0; R += colorIncrement) {
for (float G = 0; G < 1.0; G += colorIncrement) {
for (float B = 0; B < 1.0; B += colorIncrement) {
colors[i++][j] = new Color(R, G, B));
if (i == 1000) {
j++;
i = 0;
if (j == 1000) break loop;
}
}
}
}
}