I'm a noob programmer, and I'm working on a little project that involves a 2D grid of 1000 x 1000 boolean values that change based on an instruction pattern. "After x instructions, how many values in the grid are true?" That kind of thing.
I want to put a little spin on it and render the values as a grid of pixels which are black if their corresponding values are false and white if they're true and that animates in real time as instructions are processed, but I'm pretty lost -- I've never dabbled with 2D graphics in Java. I've read through Oracle's tutorial, which helped, but the way I'm doing things is sufficiently different from its demo that I still feel lost.
My most immediate problem is that I can't even seem to initialize a grid of 1000 x 1000 black pixels using a BufferedImage. Running my code yields a very tiny window with nothing (grayness) in it. Can anyone tell me what I'm doing wrong and suggest how to proceed? My code follows:
import java.awt.image.BufferedImage;
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class PixelGrid extends JPanel {
private BufferedImage grid;
// Ctor initializing a grid of binary (black or white) pixels
public PixelGrid(int width, int height) {
grid = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);
}
/**
* Fill an area with a given color
* #param color 0 for black; 1 for white
* #param x1 Starting x coordinate
* #param y1 Starting y coordinate
* #param x2 Ending x coordinate
* #param y2 Ending y coordinate
*/
public void toggleBlock(int color, int x1, int y1, int x2, int y2) {
if (color == 0) {
color = Color.BLACK.getRGB();
}
else {
color = Color.WHITE.getRGB();
}
for (int x = x1; x <= x2; x++) {
for (int y = y1; y <= y2; y++) {
grid.setRGB(x, y, color);
}
}
}
// Clear the grid (set all pixels to black)
public void clear() {
toggleBlock(0, 0, 0, grid.getWidth() - 1, grid.getHeight() - 1);
}
public static void main(String[] args) {
int width = 1000;
int height = 1000;
PixelGrid aGrid = new PixelGrid(width, height);
JFrame window = new JFrame("A Wild Pixel Grid Appears!");
window.add(aGrid); // Incorporates the pixel grid into the window
window.pack(); // Resizes the window to fit its content
window.setVisible(true); // Makes the window visible
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Note that it doesn't yet deal at all with an actual 2D array of booleans or instruction processing; I'm pretty sure I can handle that on my own, but, for now, I'm just trying to understand how to set up the graphical component.
Your code creates a BufferedImage, but then doesn't do anything with it (graphically). A few options:
Option 1: Override paintComponent of the PixelGrid class and draw the image to the JPanel
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(grid, 0, 0, this);
}
Option 2: Use a JLabel and ImageIcon
JLabel label = new JLabel(new ImageIcon(grid));
add(label);
In either case, you will have to call repaint on the Component every time the BufferedImage is changed
//some code
grid.setRGB(x, y, color);
//some more code
repaint();//or label.repaint() if Option 2
Related
I've been learning Java from a book called "Introduction to Java by David Eck". In that book, there is a Graphic with GUI chapter where the Rectangles are moved to the center. The book was explaining the stuff that was going on, but most importantly it says that "the drawFrame() subroutine will automatically be called about 60 times per second.". So, how can we lower the number of times the drawFrame() is called. The reason is I want to see the rectangles moving in slow motion.
This is the complete code:
package com.example.ch3movingrects;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.layout.BorderPane;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
/**
* When run as a program, this class opens a window on the screen that
* shows a set of nested rectangles that seems to be moving infinitely
* inward towards the center. The animation continues until the user
* closes the window.
*/
public class ch3MovingRects extends Application {
/**
* Draws a set of nested rectangles. This subroutine is called 60 times per
* second and is responsible for redrawing the entire drawing area. The
* parameter g is used for drawing. The frameNumber starts at zero and
* increases by 1 each time this subroutine is called. The parameters width
* and height give the size of the drawing area, in pixels.
* The sizes and positions of the rectangles that are drawn depend
* on the frame number, giving the illusion of motion.
*/
public void drawFrame(GraphicsContext g, int frameNumber, double elapsedSeconds, int width, int height) {
g.setFill(Color.WHITE);
g.fillRect(0,0,width,height); // Fill drawing area with white.
double inset; // Gap between edges of drawing area and the outer rectangle.
double rectWidth, rectHeight; // The size of one of the rectangles.
g.setStroke(Color.BLACK); // Draw the rectangle outlines in black.
inset = frameNumber % 15 + 0.5; // The "+ 0.5" is a technicality to produce a sharper image.
rectWidth = width - 2*inset;
rectHeight = height - 2*inset;
while (rectWidth >= 0 && rectHeight >= 0) {
g.strokeRect(inset, inset, rectWidth, rectHeight);
inset += 15; // rectangles are 15 pixels apart
rectWidth -= 30;
rectHeight -= 30;
}
}
//------ Implementation details: DO NOT EXPECT TO UNDERSTAND THIS ------
public void start(Stage stage) {
int width = 800; // The width of the image. You can modify this value!
int height = 600; // The height of the image. You can modify this value!
Canvas canvas = new Canvas(width,height);
drawFrame(canvas.getGraphicsContext2D(), 0, 0, width, height);
BorderPane root = new BorderPane(canvas);
root.setStyle("-fx-border-width: 4px; -fx-border-color: #444");
Scene scene = new Scene(root);
stage.setScene(scene);
stage.setTitle("Infinte Moving Rects"); // STRING APPEARS IN WINDOW TITLEBAR!
stage.show();
stage.setResizable(false);
AnimationTimer anim = new AnimationTimer() {
private int frameNum;
private long startTime = -1;
private long previousTime;
public void handle(long now) {
if (startTime < 0) {
startTime = previousTime = now;
drawFrame(canvas.getGraphicsContext2D(), 0, 0, width, height);
}
else if (now - previousTime > 0.95e9/60) {
// The test in the else-if is to make sure that drawFrame() is
// called about once every 1/60 second. It is required since
// handle() can be called by the system more often than that.
frameNum++;
drawFrame(canvas.getGraphicsContext2D(), frameNum, (now-startTime)/1e9, width, height);
previousTime = now;
}
}
};
anim.start();
}
public static void main(String[] args) {
launch();
}
} // end MovingRects
Any ideas on how can we lower the number of calls to drawFrame subroutine?
So I am trying to make an iterative program to show the chaos game with sierpinski's triangle. You start at (0,0) and randomly go half-way to either (0, 0), (1, sqrt(3)) or (2, 0). Repeating leaves a fractal pattern. My code in java would roughly be:
public class Sierpinski {
public static void main(String[] args) {
double x = 0;
double y = 0;
int n = 0;
while(true) {
// point on at (x,y) - this is what I need help with
// generates random number from 0 to 2
n = (int)(3 * Math.random())
// x and y randomly go halfway to one point
x += ((n == 1) ? 1 : 0) + ((n == 2) ? 2: 0);
y += ((n == 1) ? Math.sqrt(3) : 0);
x /= 2;
y /= 2;
}
}
}
How would I implement a graph with bounds 0 to 2 in x and y direction that displays these points at each iteration?
Thanks
By the way, it's a nice project ;)
To make a window in Java and draw graphics, you need two things: A JFrame and a Canvas.
JFrame is the frame of your app, just like when you are opening a a Windows app.
Canvas is a surface where we can draw graphics on it. It has a width and an height
in pixels.
To simplify things, I made a Window class that manage both of them. Here is the code:
import java.awt.Graphics;
import javax.swing.JFrame;
import java.awt.Canvas;
import java.awt.Color;
public class Window
{
//width and height of the canvas
private static final int WIDTH = 600;
private static final int HEIGHT = 600;
JFrame frame;
Canvas canvas;
public Window()
{
JFrame frame = new JFrame();
canvas = new Canvas();
canvas.setSize(WIDTH, HEIGHT);
//We add the canvas inside of the frame
frame.add(canvas);
//make the frame to fit the size of the canvas
frame.pack();
//click on the X button to close the app
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//set the app in the middle of the screen
frame.setLocationRelativeTo(null);
frame.setTitle("Sierpinski");
frame.setVisible(true);
}
//We call this method from your code
public void paint(double x, double y)
{
/*
Since your numbers range is between 0 and 2,
We need to adapt it to the size of the canvas.
The result would be random coordinates on the canvas.
*/
int coordX = (int)(x / 2 * WIDTH);
/*
Because in Java Y axe is reversed, we need to convert into its reversed value on the screen. Ex:
pixel (0,0) => pixel (0,599)
pixel (0,10) => pixel (0,589)
*/
int coordY = HEIGHT - ((int)(y / 2 * HEIGHT) + 1);
/*
Graphics is like a paintbrush for a specific object.
We are asking the canvas to give us his paintbrush.
*/
Graphics g = canvas.getGraphics();
//Try to execute the line below!
//g.setColor(Color.BLUE);
/*
Draw a rectangle of width and height of 1 pixel.
*/
g.fillRect(coordX, coordY, 1, 1);
}
}
finally, we need to create this Window object within your code, and call the paint method:
public class Sierpinski {
public static void main(String[] args) {
Window window = new Window();
double x = 0;
double y = 0;
int n = 0;
while(true) {
// point on at (x,y) - this is what I need help with
// generates random number from 0 to 2
n = (int)(3 * Math.random());
// x and y randomly go halfway to one point
x += ((n == 1) ? 1 : 0) + ((n == 2) ? 2: 0);
y += ((n == 1) ? Math.sqrt(3) : 0);
x /= 2;
y /= 2;
window.paint(x, y);
}
}
}
You should get a result like this:
The result expected
There is much you can do with it. You can check official documentation on Oracle web site or look at some tutorials on Youtube.
Canvas documentation
Graphics documentation
JFrame documentation
Have fun!
Hey Guys I have succesfully made a GUI in java that will scale polygons and circles using a slider. Everything works but I was wondering if there is a way to change the Origin point(Where it scales from). Right now it scales from the corner and I would like it to scale from the middle so I can start it in the middle and it scales out evenly. Also, If anyone could tell me an easy way to replace the Rectangle I have with an Image of some kind so you can scale the Picture up and down would be great! Thank you! Here is my code:
import javax.swing.*;
public class Fred
{
public static void main(String[] args)
{
TheWindow w = new TheWindow();
w.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //X wont close the window with out this line
w.setSize(375,375);
w.setVisible(true);
}
}
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
public class TheWindow extends JFrame
{
private JSlider slider; //declare slider
private drawRect myPanel; //declare/ create panel
public TheWindow()
{
super("Slider Example"); //make title
myPanel = new drawRect();
myPanel.setBackground(Color.green); //change background color
slider = new JSlider(SwingConstants.VERTICAL, 0, 315, 10);// restrains the slider from scaling square to 0-300 pixels
slider.setMajorTickSpacing(20); //will set tick marks every 10 pixels
slider.setPaintTicks(true); //this actually paints the ticks on the screen
slider.addChangeListener
(
new ChangeListener()
{
public void stateChanged(ChangeEvent e)
{
myPanel.setD(slider.getValue()); //Wherever you set the slider, it will pass that value and that will paint on the screen
}
}
);
add(slider, BorderLayout.WEST); //similar to init method, adds slider and panel to GUI
add(myPanel, BorderLayout.CENTER);
}
}
import java.awt.*;
import javax.swing.*;
public class drawRect extends JPanel
{
private int d = 25; //this determines the beginning length of the rect.
public void paintComponent(Graphics g)//paints circle on the screen
{
super.paintComponent(g); //prepares graphic object for drawing
g.fillRect(15,15, d, d); //paints rectangle on screen
//x , y, width, height
}
public void setD(int newD)
{
d = (newD >= 0 ? newD : 10); //if number is less than zero it will use 10 for diameter(compressed if statement)
repaint();
}
public Dimension getPrefferedSize()
{
return new Dimension(200, 200);
}
public Dimension getMinimumSize()
{
return getPrefferedSize();
}
}
Changing the "origin point" so it becomes the center of the "zoom" is basically just the process of subtract half of d from the center point.
So, assuming the the center point is 28 ((25 / 2) + 15), you would simply then subtract d / 2 (25 / 2) from this point, 28 - (25 / 2) = 15 or near enough...
I modified the paintComponent method for testing, so the rectangle is always at the center of the panel, but you can supply arbitrary values in place of the originX and originY
#Override
public void paintComponent(Graphics g)//paints circle on the screen
{
super.paintComponent(g); //prepares graphic object for drawing
int originX = getWidth() / 2;
int originY = getHeight() / 2;
int x = originX - (d / 2);
int y = originY - (d / 2);
System.out.println(x + "x" + y);
g.fillRect(x, y, d, d); //paints rectangle on screen
//x , y, width, height
}
As for scaling an image, you should look at Graphics#drawImage(Image img, int x, int y, int width, int height, ImageObserver observer), beware though, this will scaling the image to the absolute size, it won't keep the image ratio.
A better solution might be to use a double value of between 0 and 1 and multiple the various elements by this value to get the absolute values you want
I'm writing a program taken from the TV show, THE OFFICE, when they're sitting in the conference room and watching the bouncing DVD logo on the screen try to hit the corner. The square is supposed to change color when it hits an edge.
However, I'm running into a few issues.
Issue one: The Square sometimes bounces off an edge. Other times it sinks, and I can't figure out why.
Issue two: I'm not sure how to change the color of the square when it hits the edge.
Issue three: I'm trying to learn how to make a JFRAME fullscreen. And not just fullscreen on my screen but on anyone's.
THE CODE HAS BEEN POSTED TO AN ONLINE IDE FOR EASIER READING. That can be found HERE
Otherwise if you're too busy for that link. Here it is posted below.
import java.util.Random;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class BouncingMischievousSquare extends JPanel implements ActionListener {
private static final int SQUARE_SIZE = 40;
private static final int SPEED_OF_SQUARE = 6;
private int xPosit, yPosit;
private int xSpeed, ySpeed;
BouncingMischievousSquare(){
//speed direction
xSpeed = SPEED_OF_SQUARE;
ySpeed = -SPEED_OF_SQUARE;
//a timer for repaint
//http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html
Timer timer = new Timer(100, this);
timer.start();
}
public void actionPerformed(ActionEvent e){
//Screensize
int width = getWidth();
int height = getHeight();
xPosit += xSpeed;
yPosit += ySpeed;
//test xAxis
if(xPosit < 0){
xPosit = 0;
xSpeed = SPEED_OF_SQUARE;
}
else if(xPosit > width - SQUARE_SIZE){
xPosit = width - SQUARE_SIZE;
xSpeed = -SPEED_OF_SQUARE;
}
if(yPosit < 0){
yPosit = 0;
ySpeed = SPEED_OF_SQUARE;
}
else if(yPosit > height - SQUARE_SIZE){
xPosit = height - SQUARE_SIZE;
xSpeed = -SPEED_OF_SQUARE;
}
//ask the computer gods to redraw the square
repaint();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.fillRect(xPosit, yPosit, SQUARE_SIZE, SQUARE_SIZE );
}
}
MAIN CLASS
import javax.swing.*;
public class MischievousMain {
public static void main(String[] args) {
JFrame frame = new JFrame("Bouncing Cube");
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// mischievous square input
frame.add(new BouncingMischievousSquare());
frame.setVisible(true);
}
}
Anyways, Thanks for taking the time to read through my code. It's appreciated. I'm really interested in different ways to go about this.
Issue one: The Square sometimes bounces off an edge. Other times it
sinks, and I can't figure out why.
You'll hate yourself for this, but
} else if (yPosit > height - SQUARE_SIZE) {
xPosit = height - SQUARE_SIZE;
xSpeed = -SPEED_OF_SQUARE;
}
Should be...
} else if (yPosit > height - SQUARE_SIZE) {
yPosit = height - SQUARE_SIZE;
ySpeed = -SPEED_OF_SQUARE;
}
You were using xPosyit and xSpeed instead of yPosyit and ySpeed...
Issue two: I'm not sure how to change the color of the square when it
hits the edge.
Basically, whenever you detect a edge collision and change direction, simple change the panel's foreground color to something else...
This might require you to have a list of colors from which you can randomly pick or simply randomly generate the color
Then in your paintComponent method, simple use g.setColor(getForeground()) before you fill the rect...
...ps...
To make life easier, you could just write a method that either generates a random color or sets the foreground to a random color, for example...
protected void randomiseColor() {
int red = (int) (Math.round(Math.random() * 255));
int green = (int) (Math.round(Math.random() * 255));
int blue = (int) (Math.round(Math.random() * 255));
setForeground(new Color(red, green, blue));
}
Issue three: I'm trying to learn how to make a JFRAME fullscreen. And
not just fullscreen on my screen but on anyone's.
Take a look at Full-Screen Exclusive Mode API
Hello all I have this problem that I can't seem to fix. I've been given some code and have to make a "tic tac toe" game. Fairly primitive. At the moment what it want's me to do is take user input (it just asks for what row / column you want to place the marker) and it is meant to draw an oval on the appropriate square of the board. My current code is as following the work has specified that I make a new class to deal with user input.
I am currently just mucking around with trying to get it to add new items to the JFrame but am having little success. I have a dummy call for input, it doesn't check to see what I've typed it just calls an oval that SHOULD sit in the first square in the top left corner. I can get the object to draw onto the JFrame (albeit it takes up the whole frame) but it is always BEHIND the actual board (ie: if I stretch the frame I can see the circle). I've tried adding JPanels and so forth so that they sit on top of the board but so far I am having little luck.
Here is the code for creating the Oval which I was given for the task. All I am doing is instantiating a new oval with position (0,0,10,10). When it draws however it takes up the WHOLE JFrame but it is also BEHIND the actual board...... any ideas?
package lab4;
import javax.swing.*;
import java.awt.*;
/** Oval Supplier Class
* Author: David D. Riley
* Date: April, 2004
*/
public class Oval extends JComponent {
/** post: getX() == x and getY() == y
* and getWidth() == w and getHeight() == h
* and getBackground() == Color.black
*/
public Oval(int x, int y, int w, int h) {
super();
setBounds(x, y, w, h);
setBackground(Color.black);
}
/** post: this method draws a filled Oval
* and the upper left corner of the bounding rectangle is (getX(), getY())
* and the oval's dimensions are getWidth() and getHeight()
* and the oval's color is getBackground()
*/
public void paint(Graphics g) {
g.setColor( getBackground() );
g.fillOval(0, 0, getWidth()-1, getHeight()-1);
paintChildren(g);
}
}
EDIT: THIS IS NOW THE CODE WE ARE LOOKING AT--
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package lab4;
/**
*
* #author Scott
*/
import java.awt.*;
import java.util.Scanner;
import javax.swing.*;
public class GameBoard {
private JFrame win;
private int count = 1;
//Create new GUI layout
GridLayout layout = new GridLayout(3, 3);
JPanel panel = new JPanel(layout);
//Create a new Array of Rectangles
Rectangle[][] rect = new Rectangle[3][3];
public GameBoard() {
//Create new JFrame + Set Up Default Behaviour
win = new JFrame("Tic Tac Toe");
win.setBounds(0, 0, 195, 215);
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
win.setResizable(true);
//Loop goes through each line of the array. It creates a new rectangle
//determines it's colour based on modulus division
//Add's the rectangle to the JPanel.
for (int i = 0; i < rect.length; i++) {
for (int j = 0; j < rect[i].length; j++) {
rect[i][j] = new Rectangle(0, 0, 1, 1);
if (count % 2 != 0) {
rect[i][j].setBackground(Color.black);
} else {
rect[i][j].setBackground(Color.red);
}
panel.add(rect[i][j]);
count++;
}
}
//Sets the game to be visible.
win.add(panel);
win.setVisible(true);
//userInput();
}
private void userInput() {
Scanner scan = new Scanner(System.in);
}
}
Let's start with your oval class...
This is a VERY bad idea...
public void paint(Graphics g) {
// No super.paint(g) call, hello to a world of pain...
// Paint some stuff
g.setColor( getBackground() );
g.fillOval(0, 0, getWidth()-1, getHeight()-1);
// Then draw the children over it...???
paintChildren(g);
}
This is a better approach.
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor( getBackground() ); // Might want to pick a better color...
g.fillOval(0, 0, getWidth()-1, getHeight()-1);
}
At a guess, I suggest that your window is using a layout manager that is overriding your setBounds call
Answer ended up being that I need to stick with the flow layout, manually size the rectangles and use a JLayeredPane instead. Was not aware this existed, talked to my lecturer who said that my thought process was right and that was the way he intended for me to do it......what a pain but all done thanks to those that helped.