I'm currently doing a paint program where we are drawing squares where they must draw as the user drags the mouse.
My professor taught it to us using the graphics XOR mode.
I found this code and it looks much more efficient.
I am confused as to how the guide works, the way its written to me looks like as they are dragging the mouse. It would just create a bunch of different rectangles and they would all stay on the screen.
How is it that the rectangle is continuously updating to the users mouse but not staying on the screen?
Is it because the shape is not being added to an ArrayList?
public DrawingBoard() {
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
// When the mouse is pressed get x & y position
drawStart = new Point(e.getX(), e.getY());
drawEnd = drawStart;
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
// Create a shape using the starting x & y
// and finishing x & y positions
Shape aShape = drawRectangle(drawStart.x, drawStart.y, e.getX(), e.getY());
// Add shapes, fills and colors to there ArrayLists
shapes.add(aShape);
shapeFill.add(fillColor);
shapeStroke.add(strokeColor);
drawStart = null;
drawEnd = null;
// repaint the drawing area
repaint();
}
});
this.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
// Get the final x & y position after the mouse is dragged
drawEnd = new Point(e.getX(), e.getY());
repaint();
}
});
}
public void paint(Graphics g) {
// Class used to define the shapes to be drawn
Graphics2D graphSettings = (Graphics2D) g;
// Antialiasing cleans up the jagged lines and defines rendering rules
graphSettings.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// Defines the line width of the stroke
graphSettings.setStroke(new BasicStroke(2));
// Iterators created to cycle through strokes and fills
Iterator<Color> strokeCounter = shapeStroke.iterator();
Iterator<Color> fillCounter = shapeFill.iterator();
// Eliminates transparent setting below
graphSettings.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
for (Shape s : shapes) {
// Grabs the next stroke from the color arraylist
graphSettings.setPaint(strokeCounter.next());
graphSettings.draw(s);
// Grabs the next fill from the color arraylist
graphSettings.setPaint(fillCounter.next());
graphSettings.fill(s);
}
// Guide shape used for drawing
if (drawStart != null && drawEnd != null) {
// Makes the guide shape transparent
graphSettings.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.40f));
// Make guide shape gray for professional look
graphSettings.setPaint(Color.LIGHT_GRAY);
// Create a new rectangle using x & y coordinates
Shape aShape = drawRectangle(drawStart.x, drawStart.y, drawEnd.x, drawEnd.y);
graphSettings.draw(aShape);
}
}
The code will store all the rectangles user created. If you want one rectangle in the screen, you can draw the rectangle without saving it in the arraylist. delete all codes which are related to the arraylist.
Related
Up till now I have used the code given below to load image in a new new JFrame and coordinates of pixels of image are hard-coded in the code itself. However, I wish to do it dynamically i.e mark points clicked by mouse and store all the coordinates in an array or so.
The code is running properly, Image is chosen using JFileChooser in another class and the path is passed as a parameter to this class.
Please help me out.
public class GraphicsSet extends JFrame{
Font f ;
BufferedImage baseImage ;
File file ;
String Gimage;
JFrame imageFrame;
public GraphicsSet(String Image)
{
super("Frame") ;
// imageFrame = new JFrame("Click on to select points");
file = new File(Image) ;
Gimage = Image;
try
{
baseImage = ImageIO.read(file) ;
}
catch(IOException e)
{
System.out.println(e) ;
}
int imageWidth = baseImage.getWidth();
int imageHeight = baseImage.getHeight();
System.out.println(imageWidth);
setBounds(0,0,imageWidth,imageHeight) ;
setVisible(true) ;
//setDefaultCloseOperation (EXIT_ON_CLOSE) ;
}
public void paint(Graphics g)
{
g.drawImage(baseImage, 0, 0, null) ;
String[] coordsText = new String[]{
"264.33,329.94","244.24,382.57","243.00,328.88",
"264.33,329.94","272.06,331.59","278.30,341.00",
"284.28,350.02","282.18,367.78","275.24,375.79",
"272.89,378.50","269.26,380.27","266.00,381.66",
"259.36,384.50","258.52,383.52","252.00,383.09",
"244.24,382.57","238.62,383.56","232.21,377.61",
"228.01,373.71","225.52,365.66","226.13,360.00",
"226.13,360.00","227.55,354.00","227.55,354.00",
"228.20,350.96","227.74,347.67","228.74,345.00",
"230.78,339.55","237.90,331.81","243.00,328.88",
"248.10,327.42","249.02,328.30","254.00,328.88"
};
// The polygons will be stored in instances of Path2D.Float. After we create an instance
// of Path2D.Float we must set its vertices -- the easiest way to do this is through the
// moveTo(x,y) and lineTo(x,y) methods.
Path2D.Float regionOfInterest = new Path2D.Float();
// We must store the first X,Y coordinates so we can close the path, by creating a line
// to the last point to the first one.
boolean isFirst = true;
double firstX=0,firstY=0;
// For each of the X,Y coordinates, parse and store them on the Path2D.Float.
for(String s:coordsText)
{
String[] xy = s.split(",");
double x = Double.parseDouble(xy[0]);
double y = Double.parseDouble(xy[1]);
if (isFirst)
{
regionOfInterest.moveTo(x,y);
firstX = x;
firstY = y;
isFirst = false;
}
else { regionOfInterest.lineTo(x,y); }
}
// Close the path.
regionOfInterest.lineTo(firstX,firstY);
// We have the path that define the region of interest. In order to dim the image regions
// outside of this path we must create another path that contains everything but the
// region of interest.
// First we create a path for the whole image -- a rectangle with the image's coordinates.
Path2D.Float pathForWholeImage = new Path2D.Float();
pathForWholeImage.moveTo(0,0);
pathForWholeImage.lineTo(baseImage.getWidth(),0);
pathForWholeImage.lineTo(baseImage.getWidth(),baseImage.getHeight());
pathForWholeImage.lineTo(0,baseImage.getHeight());
pathForWholeImage.lineTo(0,0);
// In order to use Constructive Area Geometry (CAG) operations we must use the Area class.
// First we create an Area with the path for the whole image...
Area wholeImage = new Area(pathForWholeImage);
// .. then we subtract the region of interest from this Area.
wholeImage.subtract(new Area(regionOfInterest));
// Now we have a Path2D.Float for the region of interest and an Area for the rest of the image.
// To draw and paint them we need a graphic context, which we will get from the image itself.
Graphics2D g2d = (Graphics2D)baseImage.getGraphics();
// We want antialiasing!
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
// Fill the rest of the image with a transparent (100/255) white.
g2d.setColor(new Color(255,255,255,100));
g2d.fill(wholeImage);
// Draw the region of interest with a thick, almost opaque red line.
g2d.setStroke(new BasicStroke(5f));
g2d.setColor(new Color(255,0,0,200));
g2d.draw(regionOfInterest);
// Create a new Frame to show the results.
JFrame frame = new JFrame();
//imageFrame.setTitle("Highlighting image regions");
// Create an ImageIcon/Label to show the image.
ImageIcon icon = new ImageIcon(baseImage);
JLabel label = new JLabel(icon);
// Add it to the content pane.
imageFrame.getContentPane().add(new JScrollPane(label));
// Set some GUI parameters.
//imageFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
imageFrame.pack();
//frame.setVisible(true);
}
If I understood well what you need, this could help:
ArrayList<Float> coordsX = new ArrayList<Float>();
ArrayList<Float> coordsY = new ArrayList<Float>();
addMouseMotionListener(this);
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e){
coordsX.add(e.getX()); //Storing coordinate X
coordsY.add(e.getY()); //Storing coordinate Y
//Add this code to draw a circle each time you click.
int r = 6; //Radius of the circle/point.
int x = e.getX()-(r/2); //Position X (mouse will be in the center of the point)
int y = e.getY()-(r/2); //Position Y (mouse will be in the center of the point)
Graphics g = getGraphics(); //Getting the Graphic object
g.setColor(Color.red); //Setting color to red
g.fillOval(x, y, r, r); //Drawing the circle/point
g.dispose();
}
});
Just an example that I think solve what you need, how to store coordinates is up to you.
UPDATE:
In order to draw a point use fillOval(x, y, r, r) method from Graphics class.
Btw, if you use mouseClicked() event, sometimes you will see how your clicks appear to not have effect (to click doesn't draw or store anything), this is because the program is detecting the mouseDragged() event from MouseMotionListener Interface. To solve this you can change the current event mouseClicked() for mousePressed() event from MouseListener Interface as I already did in the code.
If you want a bigger point, just increase its radius, or decrease it if you want it smaller.
Using mouseevents, I am able to get the x and y coordinates of the frame, yet I am unable to get the x and y coordinates of the panel. The below codes are me getting the x and y coordinates of the frame.
public void mouseMoved(MouseEvent e) {
x = e.getX();
y = e.getY();
text = Integer.toString(x) +","+Integer.toString(y);
Frame.frame.repaint();
}
The below codes are me trying to get the x and y coordinates of the panel, but it's painting out 0's instead. Paint.paint is the name of my jpanel. I don't know what I'm doing wrong. Please help if you can.
public void mouseMoved(MouseEvent e) {
x = Paint.paint.getX();
y = Paint.paint.getY();
text = Integer.toString(x) +","+Integer.toString(y);
Frame.frame.repaint();
}
If I understand right, your MouseListener is registered with the JFrame, and you wish to get the x/y relative to a JPanel contained within the JFrame. The x and y within the MouseEvent refer to the Component in which the MouseListener was registered. If you have a MouseListener registered on a Parent container, and which to get the coordinates of the MouseEvent relative to a child Component, you can using SwingUtilities to convert the coordinates
public void mousePressed(MouseEvent e){
Point childCoordinate = SwingUtilities.convertPoint(parent, e.getPoint(), child);
}
I've been experimenting with different ways of moving a image over a grid of tiles for a game, but I've been unable to get a working implementation.
First I tried using a grid layout to hold a bunch of Tiles that extended Canvas and drew themselves. This drew the tiles nicely, however it seems that I am unable to draw my Player object on top of them. Originally, the Player also extended Canvas and I intended to have the widget on top of the tiles. It seems like this is impossible.
I then tried to have the Tile simply extend nothing, and just hold the image. I then hold each Tile in a 2D array and draw each Tile by a nested for loop, using the int from the for loop, multiplied by the image size, to draw Tile's Image. I put this code in a PaintListener inside of my constructor for my Map class that extended Canvas and dropped my Map onto my Shell in a Fill layout, but the PaintListener never gets called (I tested with a print statement).
What implementation could I use to draw the Tiles at the start of the game, then allow me to control the movement of my Player image?
I did something similar.
Using a PaintListener I get the calls when the Widget needs to be repainted. In my paint function, I loop over a tile array (wrapped in a World class) and draw all tiles. Afterwards I use the same technique with a worldObjects array/class:
public class WorldWidget extends Canvas {
WorldWidget() {
addPaintListener(new PaintListener() {
#Override
public void paintControl(PaintEvent e) {
WorldWidget.this.paintControl(e);
}
});
}
protected void paintControl(PaintEvent e) {
GC gc = e.gc;
for (short y = 0; y < world.getHeight(); y++) {
for (short x = 0; x < world.getWidth(); x++) {
final ITile tile = world.getTile(x, y);
final Image image = ImageCache.getImage(tile);
gc.drawImage(image, x * tileSize, y * tileSize);
}
}
// Here is used a similar loop, to draw world objects
}
}
This is obviously a condensed code example, as the class is part of an editor and reacts on mouse clicks and movement amongst other things.
When I did a tile based simulation while ago I did it this way:
I had 2 layers of the tile map - one for the terrain and second for the units.
The map itself was represented by a JPanel.
So roughly you got this for the JPanel:
public void paintComponent(Graphics graphics) {
// create an offscreen buffer to render the map
if (buffer == null) {
buffer = new BufferedImage(SimulationMap.MAP_WIDTH, SimulationMap.MAP_HEIGHT, BufferedImage.TYPE_INT_ARGB);
}
Graphics g = buffer.getGraphics();
g.clearRect(0, 0, SimulationMap.MAP_WIDTH, SimulationMap.MAP_HEIGHT);
// cycle through the tiles in the map drawing the appropriate
// image for the terrain and units where appropriate
for (int x = 0; x < map.getWidthInTiles(); x++) {
for (int y = 0; y < map.getHeightInTiles(); y++) {
if (map.getTerrain(x, y) != null) {
g.drawImage(tiles[map.getTerrain(x, y).getType()], x * map.getTILE_WIDTH(), y * map.getTILE_HEIGHT(), null);
}
}
}
if (map.getSimulationUnits() != null) {
for (Unit unit : map.getSimulationUnits()) {
g.drawImage(tiles[unit.getUnitType()], (int) Math.round(unit.getActualXCor() * map.getTILE_WIDTH()), (int) Math.round(unit.getActualYCor() * map.getTILE_HEIGHT()),
null);
}
}
// ...
// draw the buffer
graphics.drawImage(buffer, 0, 0, null);
}
Logic:
private Terrain[][] terrain = new Terrain[WIDTH][HEIGHT];
/** The unit in each tile of the map */
private Unit[][] units = new Unit[WIDTH][HEIGHT];
Then you have your game loop where you update the position of the units and other things, basically render() and update() the game. Check the links I've provided below.
NOTE
Since you are making a simple game this post about making game loops will be definitely useful for you. This hopefully also answer your question about moving the object on the map.
This site will be also very helpful since you will probably need to detect collision at some point too.
I am basically trying to do something like classic "Paint" (Microsoft's program). But i want to work with layers when painting. I thought i can use JPanel component as layer.
I was testing the code below. The goal is drawing a rectangle with mouse. There is a temp layer (temp) to draw on it while dragging the mouse, and there is actual layer (area) to draw when mouse released. But every time i start drawing a new rectangle, old ones are disappear. Also if i execute setVisible(false) and true again, everything disappears.
MouseInputAdapter mia = new MouseInputAdapter() {
private int startx = 0, starty = 0, stopx = 0, stopy = 0;
public void mousePressed(MouseEvent evt) {
startx = evt.getX();
starty = evt.getY();
}
public void mouseDragged(MouseEvent evt) {
Graphics2D tempg = (Graphics2D) temp.getGraphics();
int width = Math.abs(startx - evt.getX());
int height = Math.abs(starty - evt.getY());
int x = evt.getX(), y = evt.getY();
if(x > startx)
x = startx;
if(y > starty)
y = starty;
Rectangle r = new Rectangle(x, y, width, height);
tempg.clearRect(0, 0, getWidth(), getHeight());
tempg.draw(r);
}
public void mouseReleased(MouseEvent evt) {
Graphics2D g = (Graphics2D) area.getGraphics();
stopx = evt.getX();
stopy = evt.getY();
int width = Math.abs(startx - stopx);
int height = Math.abs(starty - stopy);
int x = startx, y = starty;
if(x > stopx)
x = stopx;
if(y > stopy)
y = stopy;
Rectangle r = new Rectangle(x, y, width, height);
g.draw(r);
}
};
area.addMouseListener(mia);
area.addMouseMotionListener(mia);
temp.addMouseListener(mia);
temp.addMouseMotionListener(mia);
What is wrong with that code?
Every time there's a repaint there's no guarantee you'll get the same graphics in the state you left it.
Two a two-step instead:
Create a List of Rectangles in your class.
In your mouse listener instead of drawing to the graphics, add a rectangle to the list.
Override paintComponent and in there draw the list of rectangles to the graphics it is passed.
Using the list is nice as items at the start of the list will be painted below ones at the end.
Classic bitmap-based graphics painting software operates on a target bitmap. You can render multiple Layers in paintComponent(), as #Keily suggests for Rectangles.
Alternatively, you may want to to look at classic object-based drawing software, outlined here.
Here's a general idea: (I'm assuming you mean layers such as in photoshop)
Set up a single JPanel for drawing.
Make a data structure containing all drawable objects you need for drawing.
In this data structure, also make a field containing an integer expressing which layer that specific drawable object is tied to.
In your paintComponent() method, check which layer is currently active and only draw the the data in that layer or below it.
This is what i was looking for; http://www.leepoint.net/notes-java/examples/mouse/paintdemo.html
My mistake; using getGraphics() method out of paintComponent() and expecting keep changes.
Why #Keilly's answer not working for me; Because if i put shapes in a list or array, when a shape changed (for example; deleting a circle's 1/4) i can't update the element in the list. Because it doesn't be same shape anymore. So i have to keep shapes as drawings, and i don't have to (and dont want to) keep them separately.
I'm doing these iTunes Stanford classes, and I've been learning beginning Java. Things are going great, but they recently introduced events-and specifically MouseEvents. I've been reading the chapters in the book, and pouring through the example code, and something is just not clicking right for me...it's always that asynchronous stuff that gives me trouble :-D
Earlier, some people mentioned it was important that I mention that the "addMouseListener" is a class in the Graphics import. As far as I can tell, that just adds a blanket mouse listener to the canvas.
I'm still real new to this, so I may not be describing things as well as I should.
This is a piece of code that I have been trying to simplify in order to better understand it. Currently, it will build a red rectangle, and I can click on it and drag it along the x axis. Great!!!
import java.awt.*;
import java.awt.event.*;
import acm.graphics.*;
import acm.program.*;
/** This class displays a mouse-draggable rectangle and oval */
public class DragObject extends GraphicsProgram {
/* Build a rectangle */
public void run() {
GRect rect = new GRect(100, 100, 150, 100);
rect.setFilled(true);
rect.setColor(Color.RED);
add(rect);
addMouseListeners();
}
/** Called on mouse press to record the coordinates of the click */
public void mousePressed(MouseEvent e) {
lastX = e.getX();
lastY = e.getY();
gobj = getElementAt(lastX, lastY);
}
/** Called on mouse drag to reposition the object */
public void mouseDragged(MouseEvent e) {
if((lastX) > 100){
gobj.move(e.getX() - lastX, 0);
lastX = e.getX();
lastY = e.getY();
}
}
/** Called on mouse click to move this object to the front */
public void mouseClicked(MouseEvent e) {
if (gobj != null) gobj.sendToFront();
}
/* Instance variables */
private GObject gobj; /* The object being dragged */
private double lastX; /* The last mouse X position */
private double lastY; /* The last mouse Y position */
}
If I drag the mouse off the canvas, I want the rectangle to stay within the canvas, and not move off it (the same behavior that a horizontal scroll bar would do if you moved beyond the scroll area with the mouse button still clicked). How can I do that?
I've been trying something along these lines, but it's not working right:
if ( ( lastX > (getWidth() - PADDLE_WIDTH) ) || ( lastX < PADDLE_WIDTH ) ) {
gobj.move(0, 0);
} else {
gobj.move(e.getX() - lastX, 0);
}
Your code is moving the rectangle relative to the last position of the mouse. This works fine when you are simply moving things, but for your needs when you want it to stop at the borders, you need to use absolute positioning.
// When the mouse is pressed, calculate the offset between the mouse and the rectangle
public void mousePressed(MouseEvent e) {
lastX = e.getX();
lastY = e.getY();
gobj = getElementAt(lastX, lastY);
}
public void mouseDragged(MouseEvent e) {
double newX;
// Assuming you can get the absolute X position of the object.
newX = gobj.getX() + e.getX() - lastX;
// Limit the range to fall within your canvas. Adjust for your paddle width as necessary.
newX = Math.max( 0, Math.min( newX, getWidth() ) );
// Set the new position of the paddle, assuming you can set the absolute position.
gobj.setX( newX );
lastX = e.getX();
lastY = e.getY();
}
}
This may not be quite what you want because as soon as you go off the edge, the object will stop moving, but then once you move back toward the canvas, your paddle will move immediately instead of waiting for the mouse to reach the same relative position to the paddle at which it started.
You can probably experiment to get it to do what you want.
In order to do this you will need to know the width of the Canvas object, i'm sure there will be a method that will provide this value. You can then check the current x location of the MouseEvent against the width of the canvas, and not increment the x coordinates of the shape object once you are past the width of the canvas. Depending on how much of the shape you want to remain in the canvas, you may need to take into account the width of the shape object as well.
One thing that helps me when dealing w/ animation and moving objects in a gui is drawing out a few scenarios on paper, and noting how the coordinates change.