I cant keep the past line i draw with "g2d.drawLine(w, x, y, z);",
I call "repaint()" to update the Jpanel and it just draws a new Line, I need to keep the past Lines i have draw.
What it should do: enter image description here
What id do:enter image description here
You could use an ArrayList of Point objects, every time you do whatever calculation you're doing, just save the end point to it. Every time the panel repaints, just draw lines between all of the points in your ArrayList, in the same order they were added.
Related
Very simple question, but I could not find the answer in JavaFX docs or StackOverflow:
I have a JavaFX Canvas filled with a graph (various calls to strokeLine(), not the issue here). I need to be able to draw a rectangle over this graph, then simply clear the rectangle, without affecting the graph in the background. (Like an undo operation).
Code to draw the rectangle ('p' and 'e' are points):
gc.rect(p.getX(), p.getY(), e.getX()-p.getX(), e.getY()-p.getY());
gc.stroke();
The most obvious answer would be to use the clearRect() method, but the problem is that it clears also the portion of the graph in the background...
So the question is: how do I clear a drawing that was made with stroke(), without affecting the other drawings in the background?
You can't do this with one canvas.
Canvas only store the result of your painting operation.
This is the interest of the canvas you can stroke million times the same line and it will only store and represent the result and doesn't consume more memory.
So you if you need to draw Something over your chart you should put an other canvas over the chart and draw on the second canvas.
It might be more straight forward and much more the JavaFX-way of doing things if you just put your canvas into a Group and then just add a Rectangle node to the Group which you can remove at any time if you want.
This can be acheaved by taking snapshot(s) of your Canvas, using the .snapshot(SnapshotParameters params, WritableImage image) method. Basicly, every time you draw something on your Canvas, you take a snapshot of it and store it somewhere (for example in a ArrayList). Then you can use those snapshots to create a 'undo' operation, by using the . drawImage(Image img, double x, double y) method of Canvas's GraphicsContext, in which you would pass the snapshot you want to go back to as the Image parameter.
I am trying to create graphics for a very simple 2D game. I have arrayLists of imageIcons that will get drawn in succession when the player moves. One for up, one for down, etc. As of right now, I do not have the finished sprites so I replaced each separate one with a temporary image.
//this is inside the constructor
playerUp.add(new ImageIcon("Player1.png"); //will be a different image, but this image still exists
playerUp.add(new ImageIcon("Player1.png"); //will be a different image, but this image still exists
playerUp.add(new ImageIcon("PLayer1.png"); //will be a different image, but this image still exists
Then I draw them to an offscreen image that I later draw to the screen to prevent flickering.
//offg is the graphics object of the offscreen image and g is for the panel
offg.clearRect(0, 0, panel.getWidth(), panel.getHeight());
playerUp.get(0).paintIcon(panel, offg, x, y);
g.drawImage(offscreen, 0, 0, panel); //this is where the error occurs!
Anyways There is some kind of error in the code I tried using:
playerUp.add(new ImageIcon(getClass().getResource("Player1.png")));
but I do not know if I needed to, or how to use it properly (when researching I saw it a lot), however, That did not fix the problem. Also, I made sure that the image is in the same folder as the .class and .java files.
Also, I created a separate class that just drew an ImageIcon straight to the panel, and it used a completely different image.
ImageIcon ii = new ImageIcon("downlaodedimage.png"); //downlaodedimage.png is the different image
ii.paintIcon(panel, g, 0, 0);
And that gives a null pointer exception. I used:
System.out.println(ii);
And it printed out downloadedimage.png, so I do not know what the issue is?
the issue is that something on that line hasn't been initialized yet. Try debugging it to find which one is null, and making sure it is initialized prior to the call. Alternatively, if this occurs during a game loop, and the variable is initialized later in the loop, you can surround it with a try - catch to wait until the variable has been initialized.
private void moveSquare(int x, int y) {
int OFFSET = 1;
if ((squareX!=x) || (squareY!=y)) {
repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
squareX=x;
squareY=y;
repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
}
}
This is a snippet from the Java-Tutorials. It's a tutorial about painting in Java.
https://docs.oracle.com/javase/tutorial/uiswing/painting/step3.html
I don't understand what the OFFSET is exactly doing.
Or what happens inside the repaint()-method.
I know, that it is necessary to paint the square correctly, because I noticed, that without the OFFSET, the Square is sometimes missing a side, which is not painted, or not deleted by the first repaint()-method.
I let the program write the variables (used for painting the rectangle (squareX...and so on)) in the console. But the width and heigth were always 20 and not 21 (width + OFFSET). Even when I read the variables inside the paintComponent()-method.
That is why I don't understand, why the square is drawn correctly the very first time, but every other time it gets repainted without the OFFSET, then it is drawn incorrectly.
And I can't look inside the repaint()-method (at least I don't know how to do it)
Another little question: Does the repaint method always "delete / overwrite" the object (in this case the square) it wants to draw, if the variables like color, position haven't changed?
This is what the paintComponent()-method is doint.
g.setColor(Color.RED);
g.fillRect(squareX,squareY,squareW,squareH);
g.setColor(Color.BLACK);
g.drawRect(squareX,squareY,squareW,squareH);
What I don't understand is, why the first repaint()-method deletes the old square. squareX/Y/W/H are the same as before. Afterwards they get the new coordinates from the mouse click, and then the square gets painted at the new location.
Why does the same code delete in the first repaint() and in the second one it creates a new square?
Sorry, if my english is bad. I'm from Germany.
Thanks in advance for all your answers!!!
I don't understand, why the square is drawn correctly the very first time,
The paintComponent(...) method is invoked WITHOUT clipping, so the entire (250 x 200) area of the panel is repainted.
but every other time it gets repainted without the OFFSET, then it is drawn incorrectly.
When you click on the panel the paintComponent(...) method is invoked WITH clipping as the two repaint(...) requests are consolidated into a single clipped painting request to make the painting more efficient.
For example, initially the square is painted at (50, 50). If you now click at (80, 80) the area repainted will be: (50, 50, 101, 101), which is the minimum area needed to clear the old square and paint the new square.
You can see the size of the clipped area change by adding the following to the paintComponent() method:
System.out.println(g.getClipBounds());
Note: for a simple painting like this you don't really need to be fancy. You could just invoke a single repaint() statement after resetting the x/y values and the entire panel will be repainted.
I don't understand what the OFFSET is exactly doing.
I think this comes down to how what a width and height value actually is.
Generally speaking, most people see a width and height as been 1 indexed (1 to width/height), where as the computer sees it as 0 indexed (0 to width/height).
So, when you define a area as been 20 pixels wide/high, is it 0-20, 0-19 or 1-20?
You can see this if you try and draw a rectangle surrounding the entire component. If you were to do something like....
g.drawRect(0, 0, getWidth(), getHeight());
the right/bottom edges would appear of the screen, instead, you need to use something more like
g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
What is happening here is, the API is allowing for both circumstances. The size of the component (defined by the width and height properties) is represented as 1 indexed, but we need to adjust for this to make it 0 indexed.
Why EXACTLY this is happening is beyond my knowledge (and generally my caring), all I care about is knowing that it happens ;)
Or what happens inside the repaint()-method.
Generally, you shouldn't. What you should care about is the fact that the API provides a consistent result, as the underlying workings of the API are delegating to native functionality (DirectX or OpenGL in most cases)
I let the program write the variables (used for painting the rectangle (squareX...and so on)) in the console. But the width and heigth were always 20 and not 21 (width + OFFSET). Even when I read the variables inside the paintComponent()-method.
That is why I don't understand, why the square is drawn correctly the very first time, but every other time it gets repainted without the OFFSET, then it is drawn incorrectly.
You wouldn't. The repaint isn't changing the physical state of the variables, but instead is asking that a small area of the component be updated, without effecting the rest of the component. What the API does is generate a clipping rectangle (see, it's another rectangle), which only allows updates to appear within that area (painting beyond it has no effect).
This is why it's called twice, once for the old position and once for the new
Another little question: Does the repaint method always "delete / overwrite" the object (in this case the square) it wants to draw, if the variables like color, position haven't changed?
It depends. Painting is considered destructive, that is, each time paintComponent is called, you are expected to repaint the entire state of the component from scratch. So, even if the state hasn't changed, the entire area must be repainted, because the paintComponent has no idea what the previous state was.
In Swing, the Graphics context is a shared resource, it is used to paint all the components for a common native peer.
You could have a look at Painting in AWT and Swing and Performing Custom Painting which might provide you with some more information about the painting process
I have started a Java Paint program that seems to be working fine... There is just one problem. In my program I have it set up so that it repaint()'s ovals using MouseListener methods and overrides paintComponent(Graphics g). The problem is when I move my mouse to fast it begins to separate my ovals instead of making one smooth line when the mouse is dragged. Is there a way to fix this.
P.S. Keep in mind that I much rather use the fillOval method not the drawLine, because I still would like to set the stroke.
Thanks in advance
See Custom Painting Approaches for the two common ways to do painting. The example draws a Rectangle without problems as the mouse moves.
You can still set a stroke to use to draw a line between two points. You should store the previous mouse position and interpolate between the last position and the current position to create a Line2D shape. Then create a stroke that has the desired width of your oval, and apply that stroke to Graphics context, then draw the line. This link has more information about strokes and shapes.
If you really want to continue drawing ovals, you could interpolate along the line between the start/end points and draw multiple ovals in a loop.
I am trying to create a pen tool using mouse listeners:
public void mouseDragged(MouseEvent e) {
imageL.setCoordinates(originalPos, e.getPoint());
imageL.repaint();
originalPos = e.getPoint();
}
The paint function in the JLabel (imageL) receives two sets of points that allow drawing a line based upon the mouse drag. The only issue is that every time the drag is performed, the new layer does not contain the line drawn from the previous mouse drag. The paint function of the JLabel is as follows:
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(drawingColour);
g2d.drawLine(originCors.x,originCors.y,endCors.x,endCors.y);
}
So essentially my question is: How do I "add" the new line to the current layer?
any help would be great,
Thanks in advance
You want to draw in a JComponent's (and JLabel extends from JComponent) paintComponent method, not the paint method unless you plan to handle all the painting of the component's borders and children. Next, you may wish to have your drawing component hold an ArrayList of Point and add Points to this array list in the mouseDragged method. Then the paintComponent method can iterate through the list and paint all the lines. If all you want to do is paint one line, then have the painting JLabel hold both points in a class field.
Michael,
For a start, in swing (a JLabel is a swing component, as apposed to the older AWT library) the recommended practice is to override the paintComponent method (not the "paint" method). See The Java Tutorials: Custom Painting for the hows & whys of it.
If you want to custom-paint a-list-of-lines then you're going to have to do just that... not just the "new" one. One way around this is to "update" an image with each new line, and then custom-paint that... this is slightly quicker as you only "paint" the each line once, at the cost of using more memory (to maintain the "backgroun image")... but this technique lends itself to "double buffering", which avoids "that flickering" you get when you draw directly to the screen. See The Java Tutorials: Working with Images for the details. It's actually pretty straight forward.
I suggest you google for "java double buffering example" (avoid Rose India, it's full of crap).
Cheers. Keith.