I'm designing a strip chart, but it isn't displaying. Its encapsulating JFrame is displaying, and paintComponent() is being called, but no gray grid lines are appearing. Why is this?
class ChartArea extends JPanel {
private int Yscl, Xscl;
private static final Color BACKGROUND_COLOR = Color.BLACK;
private static final Color LINE_COLOR = Color.GREEN;
private static final Color GRIDLINE_COLOR = Color.GRAY;
ChartArea() {
setBackground(BACKGROUND_COLOR);
setForeground(LINE_COLOR);
Yscl = 20;
Xscl = 20;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
int height = getHeight();
int width = getWidth();
double max = data.getMax(); //gets the maximum value in the data set
try {
g2d.setStroke(new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER));
//put the origin in the bottom right. Positive is up and left
g2d.translate(width, height);
g2d.scale(-1d, -1d);
//if the data set exceeds the window height, scale it down
if (max > height) {
g2d.scale(1, height / max);
}
paintGrid(g2d);
paintLine(g2d);
} finally {
g2d.dispose();
}
}
private void paintGrid(Graphics2D g) {
g.setPaint(GRIDLINE_COLOR);
//Vertical Lines
for (double pos = 0; pos > getWidth(); pos += Xscl) {
Line2D line = new Line2D.Double(pos, 0d, pos, getHeight());
g.draw(line);
}
//Horizontal Lines
for (double pos = 0; pos > getHeight(); pos += Yscl) {
Line2D line = new Line2D.Double(0d, pos, getWidth(), pos);
g.draw(line);
}
}
private void paintLine(Graphics2D g) {
g.setPaint(LINE_COLOR);
Path2D plot2 = new Path2D.Double();
if (!data.isEmpty()) {
plot2.moveTo(0.0, data.get(0));
for (int i = 1; i < data.size(); i++) {
plot2.lineTo((double) i, data.get(i));
}
g.draw(plot2);
}
}
Looks like there a typo in for loops conditions that draw lines. The loops are never executed with current conditions. If you change the conditions to pos < getWidth(); and pos < getHeight(); you should see the grid. Here is a complete method:
private void paintGrid(Graphics2D g) {
g.setPaint(GRIDLINE_COLOR);
//Vertical Lines
//for (double pos = 0; pos > getWidth(); pos += Xscl) {
for (double pos = 0; pos < getWidth(); pos += Xscl) {
Line2D line = new Line2D.Double(pos, 0d, pos, getHeight());
g.draw(line);
}
//Horizontal Lines
//for (double pos = 0; pos > getHeight(); pos += Yscl) {
for (double pos = 0; pos < getHeight(); pos += Yscl) {
Line2D line = new Line2D.Double(0d, pos, getWidth(), pos);
g.draw(line);
}
}
Here is a result:
You are overriding paintComponent to only paint your custom panel. You simply need to paint the parent instance as well.
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// custom painting
}
Related
My partner and I are trying to remake Tetris for our final project of the year in my Computer Science class we currently have a for loop that draws individual rectangles in an overwritten paint method.
private final int spacer = 30;
public int getSpacer()
{
return spacer;
}
public void paint(Graphics g) {
setBackground(Color.GRAY);
for(int i = getHeight()/2 - (spacer * 10); i < getHeight()/2 + (spacer * 10); i += spacer) {
for(int x = getWidth()/2 - (spacer * 5); x < getWidth()/2 + (spacer * 5); x += (spacer)) {
g.drawRect(x, i, (spacer), (spacer));
}
}
setForeground(Color.black);
}
The method basically takes the width and height of the window and makes a 10 x 20 grid of boxes that are 30 units, pixels I think, wide.
We'd like to make a Grid.java class that takes in color, the spacer int, and an x and y int. The constructor for Grid.java should draw the exact same thing as the code above using the for loop, but when we tried it gave us a white screen that would not resize with the window.
private final int spacer = 30;
private static Grid[][] arr = new Grid[10][20];
public int getSpacer()
{
return spacer;
}
public void paint(Graphics g) {
setBackground(Color.GRAY);
int countY = 0;
int countX = 0;
for(int y = getHeight()/2 - (spacer * 10); y < getHeight()/2 + (spacer * 10); y += spacer) {
for(int x = getWidth()/2 - (spacer * 5); x < getWidth()/2 + (spacer * 5); x += spacer) {
arr[countX][countY] = new Grid(x, y, spacer, g);
countX++;
}
countY++;
}
setForeground(Color.black);
}
*Grid.java Class*
package Tetris_Shapes;
import javax.swing.*;
import java.awt.*;
public class Grid {
private int x;
private int y;
private int side;
private Graphics g;
public Grid(int x, int y, int side, Graphics g) {
// g.drawRect(x, y, spacer, spacer);
this.x = x;
this.y = y;
this.side = side;
this.g = g;
paint(this.g);
}
private void paint(Graphics g) {
g.drawRect(x, y, side, side);
}
}
When we try and run this we get the white box that doesn't resize. My question is does anyone know of a way to get a constructor to draw shapes. Thank you in advance, this is pretty niche so I'm also going to apologize in advance.
What I should get in the end is a grid with squares that have stable colors if I resize the window. So far I get a grid that has random colors but as the everything is redrawn the colors are too. I'm thinking, maybe an array that stores the colors could work but I don't really know how to implement it in what I have so far.
public class GridRandomColors extends JFrame {
private static class Board extends JPanel {
private Rectangle MAIN_RECT;
private double BRICK_WIDTH, BRICK_HEIGHT;
private int COLS = 8;
private int ROWS = 8;
public Board() {
setBackground(Color.gray);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
drawRectangle(g);
drawBricks(g);
}
private void drawRectangle(Graphics g) {
if (getHeight() > getWidth()) {
MAIN_RECT = new Rectangle(0, 0, getWidth(), getWidth());
g.fillRect(0, (getHeight()-getWidth())/2, getWidth(), getWidth());
x = 0;
y = (getHeight()-getWidth())/2;
} else {
x = (getWidth()-getHeight())/2;
y = 0;
MAIN_RECT = new Rectangle(0, 0, getHeight(), getHeight());
g.fillRect((getWidth()-getHeight())/2, 0, getHeight(), getHeight());
}
BRICK_WIDTH = (float) MAIN_RECT.getWidth() / COLS;
BRICK_HEIGHT = (float) MAIN_RECT.getHeight() / ROWS ;
}
double spacing = 0.2;
private double x;
private double y;
private Color color;
private void drawBricks(Graphics g) {
Graphics2D brick = (Graphics2D) g.create();
for (int j = 0; j < ROWS; j++) {
for (int a = 0; a < COLS; a++) {
Random rand = new Random();
color = new Color(rand.nextFloat(), rand.nextFloat(), rand.nextFloat());
Color oldColor = g.getColor();
brick.setColor(color);
Rectangle2D.Double rect = new Rectangle2D.Double(x, y, BRICK_WIDTH - spacing*(COLS-1), BRICK_HEIGHT- spacing*(ROWS-1));
brick.fill(rect);
brick.setColor(oldColor);
x += BRICK_HEIGHT+spacing;
}
if (getHeight() > getWidth()) {
x = 0;
}
else {
x = (getWidth() - getHeight()) / 2;
}
y += BRICK_HEIGHT+spacing;
}
}
}
public GridRandomColors() {
setDefaultCloseOperation(EXIT_ON_CLOSE); //mai bine cu exit on close
setSize(800, 820);
add(new Board());
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new GridRandomColors().setVisible(true);
}
});
}
}
You can use a simple two dimensional array to store the colors and generate it in your constructor:
private static class Board extends JPanel {
// ...
private int COLS = 8;
private int ROWS = 8;
private Color[][] colors;
public Board() {
// ...
Random rand = new Random();
colors = new Color[ROWS][COLS];
for (int a = 0; a < ROWS; a++) {
for (int b = 0; b < COLS; b++) {
colors[a][b] = new Color(rand.nextFloat(), rand.nextFloat(), rand.nextFloat());
}
}
}
private void drawBricks(Graphics g) {
// ...
for (int j = 0; j < ROWS; j++) {
for (int a = 0; a < COLS; a++) {
Color oldColor = g.getColor();
brick.setColor(colors[j][a]);
// ...
}
// ...
}
}
}
I'm trying to learn Java game developing in hopes of one day going to uni. I'm currently following tutorials and learning the basics. However, after following a tutorial my render doesn't render full, it only renders half of the intended screen. The following code are the 2 classes I've used. Have I messed up with constructors, scaling, is the this.height incorrect? I cant seem to figure it out.
public class Game extends Canvas implements Runnable{
private static final long serialVersionUID = 1L;
public static int width = 300;
public static int height = width / 16 * 9;
public static int scale = 3;
private Thread thread;
private JFrame frame;
private boolean running = false;
private Screen screen;
private BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
private int[] pixels = ((DataBufferInt)image.getRaster().getDataBuffer()).getData();
public Game() {
Dimension size = new Dimension(width * scale, height * scale);
setPreferredSize(size);
screen = new Screen(width, height);
frame = new JFrame();
}
public synchronized void start() {
running = true;
thread = new Thread(this, "Display");
thread.start();
}
public synchronized void stop() {
running = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void run() {
while (running) {
update();
render();
}
}
public void update() {
}
public void render() {
BufferStrategy bs = getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
return;
}
screen.render();
for (int i = 0; i < pixels.length; i++) {
pixels[i] = screen.pixels[i];
}
Graphics g = bs.getDrawGraphics();
g.setColor(new Color(0,0,0));
g.fillRect(0, 0, getWidth(), getHeight());
g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
g.dispose();
bs.show();
}
public static void main(String[] args) {
Game game = new Game();
game.frame.setResizable(false);
game.frame.setTitle("My First Game");
game.frame.add(game);
game.frame.pack();
game.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
game.frame.setLocationRelativeTo(null);
game.frame.setVisible(true);
game.start();
}
}
public class Screen {
private int width, height;
public int[] pixels;
public Screen(int width, int height) {
this.width = width;
this.height = height;
pixels = new int[width * height];
}
public void render() {
for (int y = 0; y < height; y++ ) {
for (int x = 0; x < height; x++ ) {
pixels[x + y * width] =(0xFF00FF);
}
}
}
}
In order to render the whole screen pink you need make 2 adaptations:
1) Change the order of your operands when calculating the height on line 5, so that you multiply first and divide afterwards like this:
public static int height = width * 9 / 16;
Reason: The expression is evaluated from left to right. As all operators are integers, the result of each sub-expression is rounded down. See the difference:
300 / 16 * 9 = 18 * 9 = 162
vs
300 * 9 / 16 = 2700 / 16 = 168
2) There is a typo in the render method, where height is used twice instead of width. Therefore a rectangle is rendered. You need to change the height variable in the inner for loop to width.
public void render() {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) { // use width here instead of height
pixels[x + y * width] = (0xFF00FF);
}
}
}
So I'm working on a paint program in school and now I have to double buffer it. After a week of searching and using tutorials online I am still unable to double buffer my program properly... I have written a double buffer function but then you can't paint anything... When i disable the double buffer you can paint fine but the screen flickers. Please help
(Yes I am aware that some of my code is deprecated and I should fix that)
Code:
import java.applet.*;
import java.awt.*;
public class PaintRun extends Applet
{
static int posx = 0;
static int posy = 0;
static int posxOld = 0;
static int posyOld = 0;
static int size = 20;
static int toolNum = 0;
static boolean ready = false;
static Color col = new Color(120,120,120);
static Button clear = new Button(0,0,20,'C');
static Button sizeInc = new Button(20,0,20,'+');
static Button sizeDec = new Button(40,0,20,'-');
static Tools tool = new Tools(posx,posy);
public void paint(Graphics g)
{
drawButtons(g);
detectButtons(g);
}
private static void detectButtons(Graphics g)
{
if (clear.detect(posx,posy) == true)
{
g.setColor(Color.WHITE);
g.fillRect(0, 0, 800000, 800000);
drawButtons(g);
}
else if (sizeInc.detect(posx, posy))
{
size+=2;
drawButtons(g);
}
else if (sizeDec.detect(posx, posy))
{
size-=2;
if (size <= 2)
size = 2;
drawButtons(g);
}
else
{
if (ready == true)
{
g.setColor(Color.BLACK);
g.drawLine(posxOld, posyOld, posx, posy);
drawButtons(g);
}
}
}
private static void drawButtons(Graphics g)
{
clear.setColor(col, Color.BLACK);
sizeInc.setColor(col, Color.BLACK);
sizeDec.setColor(col, Color.BLACK);
clear.drawButton(g);
sizeInc.drawButton(g);
sizeDec.drawButton(g);
}
public boolean mouseDown(Event e, int x, int y)
{
posx = x;
posy = y;
posxOld = x;
posyOld = y;
ready = true;
repaint();
return true;
}
public boolean mouseDrag(Event e, int x, int y)
{
posxOld = posx;
posyOld = posy;
posx = x;
posy = y;
repaint();
return true;
}
// Normal Update function
public void update(Graphics g)
{
paint(g);
}
// Double buffered Update function
/*public void update(Graphics g)
{
Graphics offgc;
Image offscreen = null;
Dimension d = size();
offscreen = createImage(d.width, d.height);
offgc = offscreen.getGraphics();
offgc.setColor(getBackground());
offgc.fillRect(0, 0, d.width, d.height);
offgc.setColor(getForeground());
paint(offgc);
g.drawImage(offscreen, 0, 0, this);
}
*/
}
class Button extends Applet
{
int minX; // X coord where the button is drawn
int minY; // Y coord where the button is drawn
int maxX; // X size
int maxY; // Y size
char letter;
Color back;
Color line;
// Constructor block
public Button(char desc)
{
minX = 0;
minY = 0;
maxX = 40;
maxY = 40;
back = new Color(255,255,255);
line = new Color(0,0,0);
letter = desc;
}
public Button(int x, int y, char desc)
{
minX = x;
minY = y;
maxX = 40;
maxY = 40;
back = new Color(255,255,255);
line = new Color(0,0,0);
letter = desc;
}
public Button(int x, int y, int size, char desc)
{
minX = x;
minY = y;
maxX = size;
maxY = size;
back = new Color(255,255,255);
line = new Color(0,0,0);
letter = desc;
}
public Button(int x, int y, int sizeX, int sizeY, char desc)
{
minX = x;
minY = y;
maxX = sizeX;
maxY = sizeY;
back = new Color(255,255,255);
line = new Color(0,0,0);
letter = desc;
}
// Draws the button
public void drawButton(Graphics g)
{
g.setColor(back);
g.fillRect(minX, minY, maxX, maxY);
g.setColor(line);
g.drawRect(minX, minY, maxX, maxY);
g.drawString(Character.toString(letter), (minX+maxX/2)-4, (minY+maxY/2)+4);
}
// Sets the button colors
public void setColor(Color b, Color l)
{
back = b;
line = l;
}
// Detects if those coordinates are in the button location or not
public boolean detect(int x, int y)
{
boolean flag = false;
if (x > minX && x < (minX + maxX) && y > minY && y < (minY + maxY))
flag = true;
else
flag = false;
return flag;
}
}
I am trying to make a bar chart. Everything goes fine; the code compiles and runs successfully. But the frame (window) is not packed perfectly. There is some space at the end of the bar chart. I just want this space removed.
public class BarChart extends JPanel{
int[] percentage;
Color color;
double barOffset;
public BarChart(int[] percentage, Color color) {
this.color = color;
this.percentage = percentage;
}
public BarChart(int[] percentage) {
this.color = Color.black;
this.percentage = percentage;
}
public BarChart() {
this.color = Color.black;
}
int w = 1,h = 1;
protected void paintComponent(Graphics g) {
super.paintComponent(g);
w = getWidth();
h = getHeight();
g.setColor(color);
barOffset = w*0.05;
int barWidth = (int)(w*0.1);
for(int i = 0; i<percentage.length; i++) {
g.fillRect((int)(barOffset),(int)(h*0.95-2*percentage[i]), barWidth, 2*percentage[i]);
if(i < percentage.length-1)
barOffset = (i+2)*w*0.05 + (i+1)*(barWidth);
}
}
}
This was not a packing error, but rather you were drawing off the edge of the component. To check for packing errors, set a background color for the container that is distinct from the component color.
For the set int[] p = new int[]{100, 5, 6, 9, 1, 0, 5, 100};, your bars are being drawn as follows:
component dimensions: width=104 height=10
bar[0]: xLeft=5 yTop=-190 barWidth=10 barHeight=200
bar[1]: xLeft=20 yTop=0 barWidth=10 barHeight=10
bar[2]: xLeft=35 yTop=-2 barWidth=10 barHeight=12
bar[3]: xLeft=50 yTop=-8 barWidth=10 barHeight=18
bar[4]: xLeft=66 yTop=7 barWidth=10 barHeight=2
bar[5]: xLeft=81 yTop=9 barWidth=10 barHeight=0
bar[6]: xLeft=96 yTop=0 barWidth=10 barHeight=10
bar[7]: xLeft=111 yTop=-190 barWidth=10 barHeight=200
I think this produces what you're looking for. Drawing components can be tricky, and the way I mitigate the complexity is to keep track of my screen locations semantically.
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class BarChart extends JPanel
{
public static void main(String[] args)
{
int[] p = new int[]{100, 5, 6, 9, 1, 0, 5, 100};
JFrame f = new JFrame();
f.setBackground(Color.BLUE);
BarChart chart = new BarChart(p);
chart.setBackground(Color.RED);
f.add(chart);
f.pack();
f.show();
}
private int[] percentage;
private Color color;
private boolean padEnds = true;
public BarChart(int[] percentage, Color color)
{
this.percentage = percentage;
this.color = color;
return;
}
public BarChart(int[] percentage)
{
this(percentage, Color.BLACK);
return;
}
public BarChart()
{
this(new int[0]);
return;
}
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(this.color);
int width = super.getWidth();
int height = super.getHeight();
int topPadding = Math.round(height * 0.05f);
int barCount = this.percentage.length;
int barOffset = Math.round(width * 0.025f); // 2.5% (in pixels) reserved space on both sides of each bar == 5% between bars
int totalOffsetWidth = (barOffset * 2) * barCount;
if (!this.padEnds)
{
totalOffsetWidth -= (barOffset * 2);
}
int availableWidth = width - totalOffsetWidth;
int availableHeight = height - topPadding;
int barWidth = (int) Math.floor((float) availableWidth / (float) barCount);
int xLeft = 0;
for (int i = 0; i < barCount; i++)
{
int percent = this.percentage[i];
if (this.padEnds || (i != 0))
{
xLeft += barOffset; // Add offset here to pad left side of each bar.
}
int barHeight = Math.round(((float) availableHeight) * ((float) percent / 100f));
int yTop = topPadding + (availableHeight - barHeight);
g.fillRect(xLeft, yTop, barWidth, barHeight);
xLeft += barWidth; // advance the next drawing position
if (this.padEnds || (i != (barCount - 1)))
{
xLeft += barOffset; // Add offset here to pad right side of each bar.
}
}
return;
}
}