I am having a bit of trouble making this grid be drawn 10 pixels from the top and 10 pixels from the left of the Frame.
I can make it do it by increasing this.getY() + 10 to a higher number, just wondering why if is remove the + 10 it getting drawn off screen.
Ignore the variable names and any formatting I just threw this together
package griddrawing;
import java.awt.*;
import javax.swing.*;
public class Grid extends JFrame
{
private int TILEWIDTH;
private int TILEHEIGHT;
private int COLS;
private int ROWS;
private int defaultX;
private int defaultY;
private int currentX;
private int currentY;
public Grid()
{
setSize(800,400);
TILEWIDTH = 30;
TILEHEIGHT = 30;
COLS = 10;
ROWS = 10;
defaultX = this.getX() + 10;
defaultY = this.getY() + 10;
currentX = 0;
currentY = 0;
}
#Override
public void paint(Graphics g)
{
super.paint(g);
currentX = defaultX;
currentY = defaultY;
g.setColor(Color.black);
for(int i = 0; i < COLS; i++)
{
for(int k = 0 ; k < ROWS; k++)
{
g.drawRect(currentX - (TILEWIDTH / 2), currentY - (TILEHEIGHT / 2), TILEWIDTH, TILEHEIGHT);
g.drawString("" + k, currentX, currentY);
currentY += TILEWIDTH;
System.out.println("COL: " + i + " ROW: " + k + " Current X: " + currentX + " Current Y: " + currentY);
}
currentY = defaultY;
currentX += TILEHEIGHT;
}
}
}
Don't set the size of the frame.
Don't paint directly to the frame either.
Instead of both:
Override the paintComponent(Graphics) method of a JComponent or JPanel.
Either call theComponent.setPreferredSize(Dimension) or override that same method.
Add the custom component to the frame and call pack().
That lot should mean you no longer need to account for any offset (which might change by platform or PLAF).
Related
What would be the easiest and simplest way to keep the fill() the same after clicking (that's when it changes) and then unclicking, and leaving hover?
In this project, I simply made a grid. When the mouse hovers over a specific rect (at x,y) it changes color based on the state it is in. fill(50) is the default, fill(75) is when the mouse is hovering, and fill(100) is when the mouse clicks. But here when the mouse is unclicked it returns to hover fill until the mouse leaves the rectangle. Thanks.
int cols, rows;
int scl = 20;
void setup() {
size(400, 400);
int w = 400;
int h = 400;
cols = w / scl;
rows = h / scl;
}
void draw() {
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(75);
if (mousePressed == true){
println("Clicked at: " + xpos + " and " + ypos);
fill(100);
//here is the desired location for the fill to remain constant even
//after unclicking and leaving hover
}
println("Mouse at: " + xpos + " and " + ypos);
}else{
fill(50);
}
rect(xpos, ypos, scl, scl);
}
}
}
Stack Overflow isn't really designed for general "how do I do this" type questions. It's for specific "I tried X, expected Y, but got Z instead" type questions. But I'll try to help in a general sense:
You need to store the state of each cell in a data structure, and then use that data structure to draw your scene.
You could do this with a 2D array, where each cell in the array represents a cell in the grid. You could store the state of the cell, or the color directly.
As Kevin said, you should keep the state of your application in a matrix.
boolean[][] matrix = new boolean[21][21];
When you click on a cell, toggle it
if(!matrix[xpos/scl][ypos/scl]) {
matrix[xpos/scl][ypos/scl] = true;
} else {
matrix[xpos/scl][ypos/scl] = false;
}
Inside this loop, check if your current position can be drawn or not
if(matrix[x][y]) {
fill(204, 102, 0); // an orange color
rect(xpos, ypos, scl, scl);
}
So your draw() method should look like this
void draw() {
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(75);
if (mousePressed == true){
println("Clicked at: " + xpos + " and " + ypos);
if(!matrix[xpos/scl][ypos/scl]) {
matrix[xpos/scl][ypos/scl] = true;
} else {
matrix[xpos/scl][ypos/scl] = false;
}
fill(100);
//here is the desired location for the fill to remain constant even
//after unclicking and leaving hover
}
println("Mouse at: " + xpos + " and " + ypos);
}else{
fill(50);
}
if(matrix[x][y]) {
fill(204, 102, 0);
rect(xpos, ypos, scl, scl);
}
rect(xpos, ypos, scl, scl);
}
}
}
I am having trouble figuring out how to remove the 0's in this table. I've attempted looking it up online and have had little success figuring it out that way (probably not searching it correctly). I am attempting to get Figure #1 to appear like Figure #2 besides a few stylistic changes.
I'd appreciate any help.
Code: (http://www.buildingjavaprograms.com/DrawingPanel.java) Drawing Panel Used
import java.awt.*;
public class IfGridFor {
public static void main(String[] args) {
DrawingPanel panel = new DrawingPanel(400, 520);
panel.setBackground(Color.blue);
Graphics g = panel.getGraphics();
int sizeX = 40;
int sizeY = 40;
for (int x = 0; x < 10; x++) {
for (int y = 0; y <= 12; y++) {
int cornerX = x*sizeX;
int cornerY = y*sizeY;
if ((x + y) % 2 == 0)
g.setColor(Color.green);
else
g.setColor(Color.yellow);
g.fillRect(cornerX+1, cornerY+1, sizeX-2, sizeY-2);
g.setColor(Color.black);
g.drawString(x + " * " + y, cornerX + 5, cornerY + 15); // text is
g.drawString("= " + x * y, cornerX + 5, cornerY + 33); // offsets
}
}
}
}
Figure #1:
Figure #2:
You are almost done - all you need is changing what gets shown from x, y, x*y to (x+1), (y+1), (x+1)*(y+1), and reducing the height of the panel by one row:
DrawingPanel panel = new DrawingPanel(400, 480); // 12 rows, not 13
...
for (int x = 0; x < 10; x++) {
for (int y = 0; y < 12; y++) { // < instead of <=
...
g.drawString((x+1) + " * " + (y+1), cornerX + 5, cornerY + 15); // text is
g.drawString("" + (x+1) * (y+1), cornerX + 5, cornerY + 33); // offsets
}
}
The rest of your code (i.e. the ... parts) remain the same.
If I'm understanding your question correctly, you want to remove the top row and the left column? If so, start your for loops at one instead of zero. Also your outer loop should have the condition x <= 10 if you want the figure to include the square labelled '10'.
Then change the lines:
int cornerX = x*sizeX;
int cornerY = y*sizeY;
to:
int cornerX = (x-1)*sizeX;
int cornerY = (y-1)*sizeY;
I want to create a simple grid in Java as a map editor working with tiles. In my render method I've put this
public void render(Graphics g){
renderGrid(g,96,64);
}
private void renderGrid(Graphics g,int width, int height) {
g.setColor(Color.white);
for (int y = 0; y < height; y += 32){
for (int x = 0;x<width; x+= 32){
g.drawRect(x, y, x+32, y+32);
System.out.println("x =" + x + " y= " + y + " width= " + (x + 32 - x) + " height=" + (y + 32 - y)+ " column No= " + x/32 + " row No= " + y / 32);
}
}
}
But when I start the program, it gives me this:
Here's the debug message
x =0 y= 0 width= 32 height=32 column No= 0 row No= 0
x =32 y= 0 width= 32 height=32 column No= 1 row No= 0
x =64 y= 0 width= 32 height=32 column No= 2 row No= 0
x =0 y= 32 width= 32 height=32 column No= 0 row No= 1
x =32 y= 32 width= 32 height=32 column No= 1 row No= 1
x =64 y= 32 width= 32 height=32 column No= 2 row No= 1
Any suggestion?
Why aren't the cells square?
Your problem is that drawRect takes start locations and sizes, not start locations and end locations. So it should be:-
//drawRect(int x, int y, int width, int height)
g.drawRect(x, y, 32, 32);
Additionally
This (x + 32 - x) and this (y + 32 - y) are the same as (32)
The problem is right here:
g.drawRect(x, y, x+32, y+32);
It should be:
g.drawRect(x, y, 32, 32);
The 3rd and 4th parameters are width and height, rather than 'draw to that point'. Vis:
import java.awt.*;
import javax.swing.*;
class GridCells {
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
JOptionPane.showMessageDialog(null, new GridCellPanel());
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency
SwingUtilities.invokeLater(r);
}
}
class GridCellPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(Color.BLACK);
render(g);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 100);
}
public void render(Graphics g) {
renderGrid(g, 96, 64);
}
private void renderGrid(Graphics g, int width, int height) {
g.setColor(Color.white);
for (int y = 0; y < height; y += 32) {
for (int x = 0; x < width; x += 32) {
g.drawRect(x, y, 32, 32);
System.out.println("x =" + x + " y= " + y + " width= " +
(x + 32 - x) + " height=" + (y + 32 - y) +
" column No= " + x / 32 + " row No= " + y / 32);
}
}
}
}
After Accepted Answer I wasn't sure if you were looking to fill the entire scrren or not, so before I saw the accepted answer I was working on this one. I'll keep it up in case it's what you were looking for
Not completely sure your code logic, but I came up with my own logic that seems to be easier to follow.
Get the width of the drawing surface
private static final int SCREEN_SIZE = 300;
Set the Size you want for each sqaure.
private static final int G_W = 30;
private static final int G_H = 30;
Get the number of rows and columns by dividing the SCREEN_SIZE by the the square width/height
int columns = SCREEN_WIDTH / G_W;
int rows = SCREEN_WIDTH / G_H;
After you do the above, the loop is a lot easier to manage
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
g.drawRect(x, y, G_W, G_H);
x += G_W;
}
y += G_H;
x = 0;
}
Complete code
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Grid extends JPanel{
private static final int SCREEN_SIZE = 300;
private static final int G_W = 30;
private static final int G_H = 30;
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = 0;
int y = 0;
int columns = SCREEN_SIZE / G_W;
int rows = SCREEN_SIZE / G_W;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
g.drawRect(x, y, G_W, G_H);
x += G_W;
}
y += G_H;
x = 0;
}
}
public Dimension getPreferredSize() {
return new Dimension(SCREEN_SIZE, SCREEN_SIZE);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.add(new Grid());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
Result
I was wondering if someone could help figure this out. I've been trying to display a pyramid using nested for loops and I've only been able to get the first row (base row) working. The pyramid is suppose to have 10 rectangles at the bottom and as it increments up, the rectangle count decreases to 9, 8, 7, 6, etc. I've been looking at this for days and have had no luck.
Thank you!
public class Legos2 extends JFrame {
private int startX;
private int startY;
private int legoWidth;
private int legoHeight;
private int baseLength;
private int arcWidth;
private int arcHeight;
// Constructor
public Legos2() {
super("Jimmy's LEGOs");
startX = 20;
startY = 300;
legoWidth = 50;
legoHeight = 20;
baseLength = 10;
arcWidth = 2;
arcHeight = 2;
}
// The drawings in the graphics context
public void paint(Graphics g)
{
// Call the paint method of the JFrame
super.paint(g);
int currentX = startX;
int currentY = startY;
//row = 0 is the bottom row
for (int row = 1; row <= baseLength; row++)
{
currentX = currentX + legoWidth;
if (row % 2 == 0)
g.setColor(Color.blue);
else
g.setColor(Color.red);
System.out.println(row);
for (int col = 0; col <= baseLength; col++)
{
System.out.println(col);
g.fillRoundRect(currentX, currentY, legoWidth, legoHeight, arcWidth, arcHeight);
}
//currentY = currentY - legoHeight;
}
}
// The main method
public static void main(String[] args) {
Legos2 app = new Legos2();
// Set the size and the visibility
app.setSize(700, 500);
app.setVisible(true);
// Exit on close is clicked
app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
The currentY value should be decremented at each iteration of the outer loop: for each row, you want to restart from a lower Y. So you should uncomment the line
//currentY = currentY - legoHeight;
The currentX must be incremented after each column, so at the end of the inner loop, and not at the beginning of the outer loop. And it must be reset to the start X position of the current row before you enter the inner loop.
If you just reset currentX to startX, you'll get a wall of bricks. But you need a pyramid. So there should be one less iteration of the inner loop at each iteration of the outer loop, and the startX should also be incremented after each iteration of the outer loop:
for (int row = 1; row <= baseLength; row++) {
currentX = startX;
if (row % 2 == 0) {
g.setColor(Color.blue);
}
else {
g.setColor(Color.red);
}
System.out.println("row = " + row);
for (int col = 0; col <= baseLength - row; col++) {
System.out.println("col = " + col);
g.fillRoundRect(currentX, currentY, legoWidth, legoHeight, arcWidth, arcHeight);
currentX = currentX + legoWidth;
}
currentY -= legoHeight;
startX += legoWidth / 2;
}
I'm trying to find a white rectangle in an image. The rectangle size is fixed. This is what I've come up as of yet:
BufferedImage bImage = bufferedImage;
int height = bufferedImage.getHeight(); //~1100px
int width = bufferedImage.getWidth(); //~1600px
int neededWidth = width / 2;
int neededHeight = 150;
int x = 0;
int y = 0;
boolean breaker = false;
boolean found = false;
int rgb = 0xFF00FF00;
int fx, fy;
fx = fy = 0;
JavaLogger.log.info("width, height: " + w + ", " + h);
while ((x != (width / 2) || y != (height - neededHeight)) && found == false) {
for (int i = y; i - y < neededHeight + 1; i++) {
for (int j = x; j - x < neededWidth + 1; j++) { //Vareetu buut, ka +1 vajadziigs
//JavaLogger.log.info("x,y: " + j + ", " + i);
long pixel = bImage.getRGB(j, i);
if (pixel != colorWhite && pixel != -1) {
//bImage.setRGB(j, i, rgb);
//JavaLogger.log.info("x,y: " + (j+x) + ", " + (i+y));
breaker = true;
break;
} else {
//bImage.setRGB(j, i, 0xFFFFFF00);
}
//printPixelARGB(pixel);
if ((i - y == neededHeight-10) && j - x == neededWidth-10) {
JavaLogger.log.info("width, height: " + x + ", " + y + "," + j + ", " + i);
fx = j;
fy = i;
found = true;
breaker = true;
break;
}
}
if (breaker) {
breaker = false;
break;
}
}
if (x < (width / 2)) {
x++;
} else {
if (y < (height - neededHeight)) {
y++;
x = 0;
} else {
break;
}
}
//JavaLogger.log.info("width, height: " + x + ", " + y);
}
if (found == true) {
for (int i = y; i < fy; i++) {
for (int j = x; j < fx; j++) {
bImage.setRGB(j, i, 0xFF00FF3F);
}
}
}
JavaLogger.log.info("width, height: " + w + ", " + h);
This works ok, if the rectangle I need is close to the begining of (0;0), but as it get further away, the performance degrades quite severely. I'm wondering, if there's something that can be done?
For example, this search took nearly 8s, which is quite a lot.
I'm thinking, that this can deffinitely be done more effectively. Maybe some blob finding? Read about it, but I've no idea how to apply it.
Also, I'm new to both Java and Image processing, so any help is appreciated.
This is very rough, but successfully finds all the white pixels in the image, more checking can be done to ensure it is the size you want and everything is there but the basics are there.
PS: I have not tested with your image. r and this.rc is picture size and p and this.px is the inner rectangle size
public static void main(String[] args) {
JFrame frame = new JFrame();
final int r = 100;
final int p = 10;
NewJPanel pan = new NewJPanel(r, p, new A() {
#Override
public void doImage(BufferedImage i) {
int o = 0;
for (int j = 0; j < i.getWidth() - p; j++) {
for (int k = 0; k < i.getHeight() - p; k++) {
PixelGrabber pix2 = new PixelGrabber(
i, j, k, p, p, false);
try {
pix2.grabPixels();
} catch (InterruptedException ex) {}
int pixelColor = pix2.getColorModel()
.getRGB(pix2.getPixels());
Color c = new Color(pixelColor);
if (c.equals(Color.WHITE)) {
System.out.println("Found at : x:" + j + ",y:" + k);
}
}
}
}
});
frame.getContentPane().add(pan);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private interface A {
void doImage(BufferedImage i);
}
private static class NewJPanel extends JPanel {
private static final long serialVersionUID = -5348356640373105209L;
private BufferedImage image = null;
private int px;
private int rc;
private A a;
public NewJPanel(int r, int p, A a) {
this.px = p;
this.rc = r;
this.a = a;
}
public BufferedImage getImage() {
return image;
}
#Override public void paint(Graphics g) {
super.paint(g);
image = new BufferedImage(this.rc, this.rc,
BufferedImage.TYPE_INT_ARGB);
java.awt.Graphics2D g2 = image.createGraphics();
g2.setColor(Color.BLACK);
g2.fillRect(0, 0, this.rc, this.rc);
g2.setColor(Color.WHITE);
g2.fillRect(
new Random().nextInt(this.rc - this.px),
new Random().nextInt(this.rc - this.px),
this.px, this.px);
g.drawImage(image, this.rc, this.rc, this);
this.a.doImage(this.image);
}
}
I'm no expert but I don't think the code is the problem - you need to change your algorithm. I would start by recursively searching for a single white pixel on the 2d plane, something like:
findWhitePixel(square){
look at pixel in the middle of 'square' - if it's white return it, otherwise:
findWhitePixel(top-right-quarter of 'square')
findWhitePixel(top-left-quarter of 'square')
findWhitePixel(bottom-right-quarter of 'square')
findWhitePixel(bottom-left-quarter of 'square')
}
after you find a white pixel try travesing up, down, left and right from it to find the borders on you shape. if it's a given that there can only be rectangles - your done. if there might be other shapes (triangles, circles, etc.) you'll need some verification here.
What you are asking can be solved by the operation known as "erosion". The erosion replaces every pixel by the darkest of all pixels in the rectangle of the requested size at that location (top-left corner). Here, darkest means that non-white supersedes white.
The output of erosion is an image with W-1 columns and H-1 rows less. Any white pixel in it corresponds to a solution.
In the lucky case of a rectangle shape, erosion is a separable operation. This means that you can erode first using an horizontal segment shape, then a vertical segment shape on the output of the first erosion. For a W x H restangle size, this replaces W * H operations by W + H, a significant saving.
In the also lucky case of a binary image (non-white or white), erosion by a segment can be done extremely efficiently: in every row independently, find all contiguous runs of white pixels, and turn the W-1 rightmost ones to non-white. Do the same to all columns, shortening the white runs by H-1 pixels.
Example: find all 3x2 rectangles:
####....####
##.....#..##
#..######...
.....###....
After 3x1 erosion:
####..####
##...#####
#########.
...#####..
After 1x2 erosion:
####.#####
##########
#########.
This algorithms takes constant time per pixel (regardless the rectangle size). Properly implemented, should take a handful of milliseconds.