Hello I have an application where you click one point and then anotherand it draws a line that has a stroke of 20. I want to be able to click any part of the line and delete it.
private Shape line = new Line2D.Double(x, y, fx, fy);
public highlight(int x, int y, int fx, int fy, int page, boolean fin) {
this.x = x;
this.y = y;
this.fx = fx;
this.fy = fy;
line = new Line2D.Double(x, y, fx, fy);
this.fin = fin;
this.page = page;
}
public void tick() {
line = new Line2D.Double(x, y, fx, fy);
if (!fin) {
play.cannotdrawline = true;
fx = Comp.mx;
fy = Comp.my;
} else {
play.cannotdrawline = false;
}
if(line.intersects(Comp.mx,Comp.my,1,1) && play.candeleteline){
if(Comp.ml){
remove = true;
}
}
}
public void render(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(new BasicStroke(20, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
// g.setColor(new Color(190,240,93, 143));
if (line.intersects(Comp.mx, Comp.my,1,1) && play.candeleteline) {
g.setColor(Color.blue);
} else {
g.setColor(Color.red);
}
g.drawLine(x, y, fx, fy);
//g2d.draw(line);
}
}
This all works fine, I can delete it but I have to click one the very middle line of pixels to be able to delete it. I know how to set the stroke for drawing a line but I don't know how to set the stroke for the line shape (which is the part that checks if you can delete the line or not). How can I do this?
Thanks in advance.
I know how to set the stroke for drawing a line but I don't know how to set the stroke for the line shape (which is the part that checks if you can delete the line or not). How can I do this?
As far as I know, you can't since, similar to Color, the Stroke is a property of the Graphics2D object and not of the Shape. You can however associate a certain Stroke with certain Shape using a Map, or using a custom class if need be, for instance if you need to associate multiple properties such as Stroke and Color with Shape, just like any other Java association could be made.
Related
I'm new to Java as well as user interfaces, and I have a problem with Java Graphics. What I'm trying to achieve is drawing a grid on a JPanel, and then paint custom components into the grid.
Here is the class I want to draw the grid on (its base extends JPanel).
public class RectGridPanel extends GridPanel
{
List<Rectangle> rects;
public RectGridPanel(Simulator sim)
{
super(sim);
this.rects = new ArrayList<Rectangle>();
this.setLayout(new GridLayout(20,20));
for(int x = 1; x < 801; x += 40)
{
for(int y = 2; y < 801; y += 40)
{
Cell newCell = new RectCell(x, y, sim);
this.add(newCell);
}
}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(Color.BLACK);
for(int x = 1; x < 801; x += 40)
{
for(int y = 2; y < 801; y += 40)
{
Rectangle rect = new Rectangle(x, y, 40, 40);
g2.draw(rect);
rects.add(rect);
}
}
}
}
Here are the cells I want to draw inside the grid:
public class RectCell extends Cell
{
Rectangle shape;
public RectCell(int x, int y, Simulator sim)
{
super(x, y, sim);
shape = new Rectangle(x, y, CELL_SIZE, CELL_SIZE);
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(Color.BLACK);
g2.fill(shape);
}
}
So the grid on its own draws out fine, but as soon as I try to create cells inside it it gets disrupted like that:
public class RectCell extends Cell
{
Rectangle shape;
public RectCell(int x, int y, Simulator sim)
{
super(x, y, sim);
shape = new Rectangle(x, y, CELL_SIZE, CELL_SIZE);
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(Color.BLACK);
g2.fill(shape);
}
}
The Graphics object that is passed to this paintComponent() function defines the screen space where the current RectCell can draw. The coordinate sysetem is relative to the upper left corner of the RectCell, so drawing the background should always start at x = 0 and y = 0 or some other meaningful value. The coordinates here are not relative to the parent component as you seem to assume.
Better yet, set the background color and let Swing take care of painting it rather than painting a rectangle yourself.
It is not clear what you try to achive.
What is the reason for RectCell? Why not paint the shapes direct in the RectGridPanel?
The nested loop in the paintComponent it not a good idea. Be aware that the paintComponent is called very often and every time you in crease the rects list with new objects. The former painted rectangles get lost (qraphicaly) and new rectangles with the same parameters are blowing the list.
I'm new at Java and I'm trying to make a chess with it for training.
I'm having some trouble with the Graphic methods though.
I listed the functions which the mistake could have been in.
The code below shows a function called in main.
This function is inside my package that handle Graphic stuff.
public class GUI_main extends JComponent{
private final int tam = 100;
private final int tamTab = 8 * tam;
Image torre;
public void inicializaTabuleiro(Torre t)
{ //Sets the window properties and the "tile" proper image.
JFrame janela = new JFrame("Xadrez");
janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
janela.setSize(816, 839);
janela.getContentPane().add(new GUI_main());
janela.setLocationRelativeTo(null);
janela.setResizable(false);
janela.setVisible(true);
paintPeca(t); /*this one is the function that sets the correct image
for the tile that will be on the board*/
System.out.println("Janela inicializada com sucesso!");
}
...
next one is the "paintPeca" function, which should initialize "Image torre".
public void paintPeca(Torre t)
{
System.out.println(t.imgPeca());
torre = Toolkit.getDefaultToolkit().getImage(t.imgPeca());
}
and finally my paint function:
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(Color.black);
int x = 0, y = 0;
while(y<tamTab) //draw board
{
if(y%200 == 0)
{
g2.draw(new Rectangle2D.Double(x, y, tam, tam));
x = x + tam;
g2.fill(new Rectangle2D.Double(x, y, tam, tam));
x = x + tam;
}
else
{
g2.fill(new Rectangle2D.Double(x, y, tam, tam));
x = x + tam;
g2.draw(new Rectangle2D.Double(x, y, tam, tam));
x = x + tam;
}
if(x == tamTab)
{
y = y + tam;
x = 0;
}
}
x = 14;
y = 19;
g2.drawImage(torre, x, y, this); //draw tower
g2.finalize();
...
Together these functions should have draw a board and a tower on it.
It's showing me the board, but not the tower (Image torre), and I do not have a clue on the reason.
"t.imgPeca()" returns a string (path to image), and it's correct, as I have tested before.
Since I'm new at Java this might be some kind of of silly mistake, but I have not found the answer in other pages of stack that talked about the drawImage function.
im trying to paint random (not yet) circles on a JPanel through click on a JMenu.
Im using a JTextField (and have to keep this) for some output.
Here is my Class:
class RandomDrawer extends JPanel implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
//double x = Math.random();
//double y = Math.random();
Random generator = new Random();
int x = generator.nextInt(100)+1;
int y = generator.nextInt(100)+1;
//System.out.printf("x = %d y = %d\n", x, y);
status.setText(String.format("rnd draw x: %d y: %d", x, y));
Graphics2D gg = (Graphics2D) canvas.getGraphics();
gg.setColor(Color.BLACK);
gg.drawOval(50, 50, 50, 50);
}
}
As long i let the line
status.setText(String.format("rnd draw x: %d y: %d", x, y));
stay in there i get nothing drawn. Without it i get my circle, im not sure what the problem is. I cant figure out why nothing is drawn.
Thanks a lot
EDIT:
Hello, i tried to understand the given informations.
Sadly I have to draw using the Graphics2D class, so i guess i can not draw using shapes. So i tried this, sadly it wont draw yet, can u give me some tips?
I tried to create a new class DrawShape, my thought was that i could keep track with those objects.
In my understanding there should be a drawn oval right now
gg.drawOval(100,100,100,100);
Thank you.
class DrawShape {
public DrawShape(String string) {
// TODO Auto-generated constructor stub
}
}
class RandomDrawer extends JPanel implements ActionListener {
/* (non-Javadoc)
* #see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
private List<DrawShape> shapes = new ArrayList<DrawShape>();
public void addShape(DrawShape s) {
shapes.add(s);
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
final Graphics2D gg = (Graphics2D) g;
gg.setColor(Color.BLACK);
gg.drawOval(100, 100, 100, 100);
}
#Override
public void actionPerformed(ActionEvent e) {
Random generator = new Random();
int x = generator.nextInt(100)+100;
int y = generator.nextInt(100)+100;
if (e.getActionCommand()==("Draw RandomCircle")) {
System.out.printf("x = %d y = %d\n", x, y);
status.setText(String.format("rnd draw x:%d y:%d ", x, y));
DrawShape circle = new DrawShape("Circle");
addShape(circle);
int count = shapes.size();
System.out.printf("objects in array: %d\n", count);
}
else if (e.getActionCommand()==("Draw RandomRectangle")) {
System.out.printf("x = %d y = %d\n", x, y);
//status.setText(String.format("rnd draw x: y: "));
//Graphics2D gg = (Graphics2D) canvas.getGraphics();
//gg.setColor(Color.BLACK);
//gg.drawRect(x, y, generator.nextInt(x), generator.nextInt(y));
}
}
}
Painting and such happens event-driven. If a a piece of a component needs to be redrawn its paintComponent method is called.
This means you need a component that nows how to draw by for instance:
public class DrawShape {
public final String text;
public final Color color;
public final Shape shape;
public DrawShape(String text, Color color, Shape shape) {
this.text = text;
this.color = color;
this.shape = shape;
}
}
public class CanvasWithShapes extends JPanel {
private List<DrawShape> shapes = new ArrayList<>();
public void addShape(DrawShape shape) {
shapes.add(shape);
}
#Override
public void paintComponent(Graphics g) {
final Graphics2D gg = (Graphics2D) g;
// Java 8: shapes.stream().forEach((shape) -> gg.draw(shape));
for (DrawShape drawShape : shapes) {
gg.setColor(drawShape.color);
gg.draw(drawShape.shape);
Rectangle bounds = shape.getBounds();
gg.drawString(shape.text, bounds.x+ 10, bounds.y + 20);
}
}
}
And then just add shapes to be redrawn a bit later.
Shape oval = ...;
c.add(oval);
c.repaint(50L); // A bit later
More detailed
A Shape has many implementations of interest like rectangle and oval.
Graphics2D can draw and fill a Shape. So in your case it would be ideal to add such a Shape. Maybe together with color and text. So I took your DrawShape class to hold these properties.
Random generator = new Random();
int x = generator.nextInt(100)+100;
int y = generator.nextInt(100)+100;
if (e.getActionCommand().equals("Draw RandomCircle")) {
System.out.printf("x = %d y = %d\n", x, y);
status.setText(String.format("rnd draw x:%d y:%d ", x, y));
int w = generator.nextInt(100) + 10;
int h = w;
Shape circle = new Ellipse2D.Double(x, y, w, h);
addShape(new DrawShape(text, Color.BLACK, circle));
int count = shapes.size();
System.out.printf("objects in array: %d\n", count);
} else if (e.getActionCommand().equals("Draw RandomRectangle")) {
System.out.printf("x = %d y = %d\n", x, y);
generator.nextInt(y));
int w = generator.nextInt(100) + 10;
int h = generator.nextInt(100) + 10;
Shape rect = Rectangle2D.Double(x, y, w, h)
addShape(new DrawShape(text, Color.BLACK, rect));
}
Graphics2D gg = (Graphics2D) canvas.getGraphics();
Don't use the getGraphics() method to do painting. The painting is temporary. It will be lost if you resize the frame for example.
Instead you need to override the paintComponent() method of your panel.
If you want to paint multiple objects then you need to keep track of each object. Check out Custom Painting Approaches for the two common ways to do this:
keep of List of Objects to paint and then iterate through the List each time the component is repainted.
paint the Object directly to a BufferedImage and then just paint the BufferedImage.
The example paints Rectangles. Basically you need a method like the addRectangle(...) method to add a new object to paint. So every time you click your button you add the new random shape.
Presumably, your problem arises from the setText() invocation modifying the Graphics object in some unexpected way. It is rarely appropriate to use getGraphics() in your own code. Instead, paint with the Graphics that is given to you.
Your approach is anyway flawed. If you manage to draw on a GUI component only once, as you are trying to do, then whatever you have drawn will disappear when the component is next repainted. Repainting can happen for a wide variety of reasons, many of them unrelated to the program's own behavior.
What you need to do is store some kind of data that the component's paintComponent() method will rely upon to do your custom painting every time. It follows that you will need to override the paintComponent() method of the component on which you want the circles to be drawn. For example, you might create a class that records all the needed drawing details for one circle, and give RandomDrawer a List of those objects as a member variable. The action listener manipulates that list appropriately and schedules a repainting, and paintComponent() is overridden to perform the actual painting.
This question already has an answer here:
Clear portion of graphics with underlying image
(1 answer)
Closed 7 years ago.
I'm in the process of making a 2D game in which a player roams around a maze.
I want to implement some sort of "darkness", even something as simple as a transparent shape around the player surrounded by black, like this:
The problem I've found using Swing is that, while this is possible, it means having to redraw everything, which produces an annoying "flickering" effect every time it happens. Is there a way to make some sort of overlay, or just a good way of doing this in general in Swing? I'm not very experienced with GUI/visual stuff right now so I'd like to stick with Swing if possible.
EDIT: This is my method to paint the background, i.e. the floor, walls and exit:
public final void paintBG(Graphics g){
g.setColor(Color.LIGHT_GRAY); // Screen background
g.fillRect(0, 0, getWidth(), getHeight());
// Draw the Walls of the maze
// scalex and y are for scaling images/walls within the maze since I let users specify how big they want the maze
for (int j = 0; j < this.height; j++, y += scaley) {
x = 20;
for (int i = 0; i < this.width; i++, x += scalex) {
if (!(maze[j][i].northwall.isBroken())) // If the north wall isn't broken
{
g.drawImage(walltile, x, y, scalex, scaley / 5, null); // Draw a wall there (image, xpos, ypos, width, height, observer)
}
if (!(maze[j][i].eastwall.isBroken())) // etc
{
g.drawImage(walltile, x + scalex, y, scalex / 5, scaley, null);
}
if (!(maze[j][i].southwall.isBroken())) {
g.drawImage(walltile, x, y + scaley, scalex, scaley / 5, null);
}
if (!(maze[j][i].westwall.isBroken())) {
g.drawImage(walltile, x, y, scalex / 5, scaley, null);
}
if ((j == mazeinfo.getTargetM()) && (i == mazeinfo.getTargetN())) {
// Draw the exit
g.drawImage(jeep, x + (scalex / 2), y + (scaley / 2), cx, cy, null);
g.setColor(Color.LIGHT_GRAY);
if (maze[j][i].northwall.isEdge()) {
// Paint over the edge creating a 'way out'
g.fillRect(x, y, scalex, scaley / 4);
} else if (maze[j][i].eastwall.isEdge()) {
g.fillRect(x + scalex, y, scalex / 4, scaley);
} else if (maze[j][i].southwall.isEdge()) {
g.fillRect(x, y + scaley, scalex, scaley / 4);
} else if (maze[j][i].westwall.isEdge()) {
g.fillRect(x, y, scalex / 4, scaley);
}
}
}
}
}
I then have "paintPlayer" and "paintEnemy" methods to paint those sprites each time they move. The background only gets painted once, at the start.
Possibilities:
You may be drawing directly in a top level window such as a JFrame. If so, don't draw in the paintComonent method of a JPanel so that you use the automatic double buffering availabe.
You may be reading in an image from within a painting method, and if so, don't. These methods must paint and paint only and must be blindingly fast.
You may not be using a BufferedImage in your painting method but creating an image de-novo, and if so, don't. Draw the BufferedImage using Graphics#drawImage(...).
Perhaps your animation code is off. You may be calling repaint() from within paint or paintComponent, something that should never be done.
And the possible guesses can go on and on...
Edit
Your code shows that you may be re-paint the maze with every painting iteration -- don't do this. Instead draw the above into a BufferedImage, and draw that image within your paintComponent method. Then change the BufferedImage if the walls structurally change.
Note that the maze's logical structure (the non-visual data that tells which wall is open, which is closed) should be part of your program's data, and not its code.
Here in an example of using a LayerUI from Oracle's Swing UI documentation. Just change the AlphaComposite constant to something darker.
The following is a LayerUI subclass that draws a translucent circle wherever the mouse moves inside a panel.
class SpotlightLayerUI extends LayerUI<JPanel> {
private boolean mActive;
private int mX, mY;
#Override
public void installUI(JComponent c) {
super.installUI(c);
JLayer jlayer = (JLayer)c;
jlayer.setLayerEventMask(
AWTEvent.MOUSE_EVENT_MASK |
AWTEvent.MOUSE_MOTION_EVENT_MASK
);
}
#Override
public void uninstallUI(JComponent c) {
JLayer jlayer = (JLayer)c;
jlayer.setLayerEventMask(0);
super.uninstallUI(c);
}
#Override
public void paint (Graphics g, JComponent c) {
Graphics2D g2 = (Graphics2D)g.create();
// Paint the view.
super.paint (g2, c);
if (mActive) {
// Create a radial gradient, transparent in the middle.
java.awt.geom.Point2D center = new java.awt.geom.Point2D.Float(mX, mY);
float radius = 72;
float[] dist = {0.0f, 1.0f};
Color[] colors = {new Color(0.0f, 0.0f, 0.0f, 0.0f), Color.BLACK};
RadialGradientPaint p =
new RadialGradientPaint(center, radius, dist, colors);
g2.setPaint(p);
g2.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, .6f));
g2.fillRect(0, 0, c.getWidth(), c.getHeight());
}
g2.dispose();
}
#Override
protected void processMouseEvent(MouseEvent e, JLayer l) {
if (e.getID() == MouseEvent.MOUSE_ENTERED) mActive = true;
if (e.getID() == MouseEvent.MOUSE_EXITED) mActive = false;
l.repaint();
}
#Override
protected void processMouseMotionEvent(MouseEvent e, JLayer l) {
Point p = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), l);
mX = p.x;
mY = p.y;
l.repaint();
}
}
To keep the spotlight's center updated on your player, create an event for player movement and register the LayerUI to listen for updates. See the setLayerEventMask() example in the JLayer link below.
source: How to Decorate Components with the JLayer Class
I made a java paint application and I made a rainbow brush function; however, I want to make the randomized colors into a smooth gradient. It is currently just printing ovals of differently colors and you can notice each distinct oval. Is there a way to make it a gradient?
Paint Project - CLICK HERE TO SEE PROGRAM
My Rainbow Fuction:
public void rainbow() {
Random generator = new Random();
int r = generator.nextInt(256);
int g = generator.nextInt(256);
int b = generator.nextInt(256);
Color color = new Color(r, g, b);
g2.setPaint(color);
}
My Mouse Listeners:
public DrawArea() {
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
// save coord x,y when mouse is pressed
oldX = e.getX();
oldY = e.getY();
}
});
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
// coord x,y when drag mouse
currentX = e.getX();
currentY = e.getY();
if (g2 != null) {
// draw oval if g2 context not null
g2.drawOval(oldX, oldY, 40, 40);
g2.fillOval(oldX, oldY, 40, 40);
// refresh draw area to repaint
repaint();
// store current coords x,y as olds x,y
oldX = currentX;
oldY = currentY;
}
}
});
}
Paint Component:
public void paintComponent(Graphics g) {
if (image == null) {
image = createImage(getSize().width, getSize().height);
g2 = (Graphics2D) image.getGraphics();
clear();
}
// enable antialiasing
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.drawImage(image, 0, 0, null);
}
I presume you're calling your rainbow function every ticks, which is why you get the effect as such.
To create the illusion of having a gradient, the r,g,b values must be changing slow enough to have the desired effect.
One way you can do that is by storing the value you want to LERP (see: https://en.wikipedia.org/wiki/Linear_interpolation) to.
//declare a variable to store the destinated Color;
public Color colorToBeLerped = null;
public Color currentColor = null;
public Color originalColor = null;
public final float deltaFactor = 0.2;
public void rainbow() {
if(currentColor==null||currentColor.equals(colorToBeLerped)){
Random generator = new Random();
int r = generator.nextInt(256);
int g = generator.nextInt(256);
int b = generator.nextInt(256);
colorToBeLerped = new Color(r, g, b);
originalColor = colorToBeLerped;
}
if(currentColor==null){
currentColor=colorToBeLerped;
}else{
//using the Color constructor that accepts float arguments, divide 255
currentColor= new Color((colorToBeLerped.getRed()-originalColor.getRed()*deltaFactor)/255,
(colorToBeLerped.getGreen()-originalColor.getGreen()*deltaFactor)/255,
(colorToBeLerped.getBlue()-originalColor.getBlue()*deltaFactor)/255);
}
g2.setPaint(currentColor);
}
Explaination:
1.Keep track of the color you want to lerp to, the current color you are in and the original color when the randomize function is called.
2.When we the first time rainbow function is called, current color will be set to the randomized color.
3.For every tick, if the current Color is not the destination color, we will increment by 1/5 of how much the difference is between original Color and destination Color.
4.A constant delta factor of 0.2 means that we will need 5 ticks to get from one color to another color, the lesser this variable is, the longer it will get from your original color to the destination color.
5.If we have reached the color, we will elect another new destination color.
*Not tested but I think you can figure out the rest