Graphics not drawing in paintComponent(Graphics)? - java

I'm working on a custom Swing Component for my application, and I started drawing things with the public void paintComponent(Graphics g). Everything works fine except for the fact that I can't draw any rectangles. I think the problem is with the getX() and getY() part, but I don't know that for sure. Here's my code:
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (mouseEntered) {
g.setColor(HIGHLIGHTED_COLOR);
} else {
g.setColor(BACKGROUND_COLOR);
}
g.fillRect(getX(), getY(), getWidth(), getHeight());
//Draw rest of stuff (works fine)
The API says that it is supposed to be used like this: g.fillRect(x, y, width, height), and that's what I'm doing.
The rest of the drawing works perfectly, but I can't figure out why this isn't drawing. Any suggestions?

I'm not exactly sure how your Component is defined, but the default value for a Component's getX() method is the X coordinate of the Component's upper left hand corner (relative to the root Component).
When you are drawing in a Component's paintComponent(Graphics) method in Swing, the origin of the Graphics context that you are drawing to is typically located at the top-left of the Component itself, not the root Component.
So by doing this call:
g.fillRect(getX(), getY(), getWidth(), getHeight());
You are likely drawing the rectangle outside of the clip bounds of the Component!
(e.g. if the Component is located at 100, 100 and it has a width of 20 and height of 20, the rectangle you are drawing, in absolute coordinates, is at (200, 200) to (220, 220))
If you want to draw a rectangle that encompasses the entire component, you may want to try something more like this:
g.fillRect(0, 0, getWidth(), getHeight());
This will draw from the origin (again, likely the top-left hand corner of the Component) down to the width and height of the component.
(Using previous example: Component is at 100, 100, and width/height of 20, the rectangle this would draw is at (100, 100) to (120, 120))
Hope this helps =)

Related

how to draw a black rectangle over Image icon?

in my application I have a cross road picture in the background and I want to draw traffic lights on the top of it (black rectangle with 3 circles)
The problem is, I cannot see the rectangle at all, as if it was under the image or something. And if I switch the order in which the items are painted, I get all black image.
Do you have any idea how this can be solved?I am new to graphics and searched similar questions, but none helped me.
Thank you.
public MainFrame() throws HeadlessException {
super("semafor");
crossroad = new ImageIcon("cross.png");
initFrame();
initComponents();
sem1 = new Semafor(true, 100, 100);
add(sem1);
repaint();
setVisible(true);
}
//here I paint the image
#Override
public void paint(Graphics g) {
super.paint(g);
g.drawImage(crossroad.getImage(), 0, 45, this);
}
//and in class Semafor i paint the actual traffic lights
#Override
public void paint(Graphics g) {
g.setColor(Color.black);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.darkGray);
//and then the circles
}
The first thing I'm noticing is that you are calling <unknown>.getWidth() and <unknown>.getHeight() for the rectangle size. If it's covering the entire image, this suggests that it is getting that width and height from the panel it is being drawn on.
A simple stack trace,
(new Exception).printStackTrace();
or
Thread.dumpStack();
will tell you as much. You could also query the width and height with a System.out call to verify that you're getting the values you're expecting, or, if this really gets out of control, learn to use JUnit and the assert statement. Honestly, though, it looks like you're just accidentally calling the wrong method.

2D clip area to shape

I'm quite new to graphics in java and I'm trying to create a shape that clips to the bottom of another shape. Here is an example of what I'm trying to achieve:
Where the white line at the base of the shape is the sort of clipped within the round edges.
The current way I am doing this is like so:
g2.setColor(gray);
Shape shape = getShape(); //round rectangle
g2.fill(shape);
Rectangle rect = new Rectangle(shape.getBounds().x, shape.getBounds().y, width, height - 3);
Area area = new Area(shape);
area.subtract(new Area(rect));
g2.setColor(white);
g2.fill(area);
I'm still experimenting with the clip methods but I can't seem to get it right. Is this current method ok (performance wise, since the component repaints quite often) or is there a more efficient way?
I think your original idea about using the clip methods was the right way to do it. This works for me:
static void drawShapes(Graphics2D g, int width, int height,
Shape clipShape) {
g.setPaint(Color.BLACK);
g.fillRect(0, 0, width, height);
g.clip(clipShape);
int centerX = width / 2;
g.setPaint(new GradientPaint(
centerX, 0, Color.WHITE,
centerX, height, new Color(255, 204, 0)));
g.fillRect(0, 0, width, height);
g.setPaint(Color.WHITE);
int whiteRectHeight = height * 4 / 5;
g.fillRect(0, whiteRectHeight,
width, height - whiteRectHeight);
}
Is this current method ok (performance wise, since the component repaints quite often) ..
Subtracting shapes is how I'd go about it. The objects could be a few instances or (possibly) a single instance that is transformed as needed.
A text demo., using scaling & fading.
Here's one with simple lines (..and dots, ..and it is animated).
Of course, if the image is purely additive, use a BufferedImage as the canvas & display it in a JLabel/ImageIcon combo. As in both of those examples.

BasicStroke causes offcenter

Changing the BasicStroke of a Graphics2D object to anything other than 1 causes it to not draw something on the center of a JPanel on startup.
This is a JPanel which is on a JFrame. This is the basic idea of my project, but it is not the entire thing.
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
if(this.centered){
this.myShape.setCenterX(this.getWidth()/2);
this.myShape.setCenterY(this.getHeight()/2);
}
g2.setStroke(new BasicStroke(3)); //new BasicStroke(1) works fine
g2.draw(this.myShape);
}
When you click and drag the myShape, myShape will immediately jump to the center. But when I initially compile and run it, paintComponent() paints it about a centimeter above the center of the screen if the stroke is not 1.
Is there something wrong with how I'm centering? I defined the MyShape class, so there could be an error there. Maybe the distance between the center and the drawing point is the space between JPanel and the top of the JFrame? How do I fix it?
Edit: added picture
http://s21.postimage.org/dfpmz73et/Untitled_1.png
The first shape is right where I want it. The other two are above where I want it. But it appears the displacement from the center are the same regardless of stroke size.
Yes, I believe this is the normal behaviour for a shape. It assumes an outline of 1 pixel. So you need to change the center calculation when you know you are going to change the basic stroke size. Something like:
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
BasicStroke stroke = new BasicStroke(3);
int adjustment = stroke.getLineWidth() - 1;
if(this.centered){
this.myShape.setCenterX(this.getWidth() + adjustment / 2);
this.myShape.setCenterY(this.getHeight() + adjustment / 2);
}
g2.setStroke(stroke);
g2.draw(this.myShape);
}

Drawing an object with rotation without rotating the entire screen

I am trying to rotate a player to follow the mouse. To do this I use a Graphics obj casted to a Graphics2D object and use the rotate method. Here is my Panel draw:
public void paint(Graphics g){
g.setColor(Color.white);
g.clearRect(0, 0, this.getWidth(), this.getHeight());
player.draw(g);
enemy.draw(g);
mouseSelection.draw(g);
wallBoard.draw(g);
//draw the existing walls
for(Wall w : walls)
w.draw(g);
//draw the potential wall
potentialWall.draw(g);
//draw the lineWalls
for(Wall w : lineWalls)
w.draw(g);
}
All my rotation stuff is happening in player.draw(g), but I figured it would be better to have more information than less. Here is my player.draw(g)
public void draw(Graphics g){
//draw the player as a circle for now
g.setColor(Color.black);
Graphics2D g2d = (Graphics2D)g;
g2d.drawOval(getX(), getY(), 20, 20);
sword.draw(g2d);
g2d.rotate(rotation);
g2d.rotate(0);
}
I have tried many combinations of the g2d.rotate and drawing the shapes. Any advice as how i can rotate the player and the sword, but not the entire world itself?
I would try drawing your player to its own image (with its own graphics object), rotating THAT image, and then drawing that image on your main graphics.
You'll run into some potentially annoying hurdles to get across, like transparency on the temporary image, but they aren't anything that can't be gotten around with a little blood sweat and tears.

How can I draw on JPanel using another quadrant for the coordinates?

I would like to draw some shapes on a JPanel by overriding paintComponent. I would like to be able to pan and zoom. Panning and zooming is easy to do with AffineTransform and the setTransform method on the Graphics2D object. After doing that I can easyli draw the shapes with g2.draw(myShape) The shapes are defined with the "world coordinates" so it works fine when panning and I have to translate them to the canvas/JPanel coordinates before drawing.
Now I would like to change the quadrant of the coordinates. From the 4th quadrant that JPanel and computer often uses to the 1st quadrant that the users are most familiar with. The X is the same but the Y-axe should increase upwards instead of downwards. It is easy to redefine origo by new Point(origo.x, -origo.y);
But How can I draw the shapes in this quadrant? I would like to keep the coordinates of the shapes (defined in the world coordinates) rather than have them in the canvas coordinates. So I need to transform them in some way, or transform the Graphics2D object, and I would like to do it efficiently. Can I do this with AffineTransform too?
My code for drawing:
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setColor(Color.blue);
AffineTransform at = g2.getTransform();
at.translate(-origo.x, -origo.y);
at.translate(0, getHeight());
at.scale(1, -1);
g2.setTransform(at);
g2.drawLine(30, 30, 140, 20);
g2.draw(new CubicCurve2D.Double(30, 65, 23, 45, 23, 34, 67, 58));
}
This is an off the cuff answer, so it's untested, but I think it will work.
Translate by (0, height). That should reposition the origin to the lower left.
Scale by (1, -1). That should flip it about the x axis.
I don't think the order of operations matters in this case.

Categories

Resources