I'm trying to have an image follow my cursor, smoothly moving where it is located and turning to face it.
When i'm near the origin, it works flawlessy: the image will flip completely without issues, however the further i go from the origin of the window, the less the image turns. When i'm at the other side of the screen, instead of flipping over it'll rotate 5°-15° instead.
I'd love if somebody could point out what the issue is =)
Here's my current code for the image:
lblRover = new JLabel(sees) { // sees is an ImageIcon
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
AffineTransform aT = g2.getTransform();
Shape oldshape = g2.getClip();
double x = getWidth()/2.0;
double y = getHeight()/2.0;
aT.rotate(Math.toRadians(degrees), x, y);
g2.setTransform(aT);
g2.setClip(oldshape);
super.paintComponent(g);
}
};
lblRover.setSize(179, 180);
lblRover.setLocation(500, 300);
JFrame.getFrames()[0].add(lblRover);
And this is the code that handles the rotation, plus its movement:
NewJFrame.PInf = MouseInfo.getPointerInfo();
/* The frame is contained in NewJFrame, PInf is a PointerInfo */
p = (NewJFrame.PInf.getLocation()); // p is a point
p.x-=NewJFrame.getWindows()[0].getLocationOnScreen().x;
p.y-=NewJFrame.getWindows()[0].getLocationOnScreen().y;
//i subtract the location of the window relative to the screen
img = NewJFrame.lblRover.getLocation();
img.x+=NewJFrame.lblRover.getWidth()/2;
img.y+=NewJFrame.lblRover.getHeight()/2;
// img will be the point with the center of my image
sas=getAngle(p,img); // sas is a float variable
NewJFrame.degrees=sas;
//
// From now on i move the image
//
diffx = p.x-img.x;
diffy = p.y-img.y;
diffx/=80; // 80 is an arbitrary number to smooth the effect
diffy/=80; // high numbers will make the image not move at all
Point var = new Point(NewJFrame.lblRover.getLocation());
// I may have to use img here or subtract width and height /2
var.x+=diffx;
var.y+=diffy;
NewJFrame.lblRover.setLocation(var);
// A 5ms sleep to smooth the movement
// I also refresh a debug label
// Also i do refresh the frame, otherwise nothing happens
NewJFrame.jLabel1.setText(""+NewJFrame.lblRover.getLocation().y
+"/"+ p.y+" "+NewJFrame.lblRover.getLocation().x +"/"+ p.x );
NewJFrame.getFrames()[0].repaint();
try {
sleep(5);
} catch (InterruptedException ex) {
Logger.getLogger(thread.class.getName()).log(Level.SEVERE, null, ex);
}
Well, after multiple tries i've narrowed it down.
Now, the rotation code is put last, before the repaint, and it looks like this:
sas=this.getAngle((float)diffy,(float)diffx);
NewJFrame.degrees=sas;
I've also created this new method that uses two float instead of two points:
public float getAngle(Float x, Float y) {
float angle = (float) Math.toDegrees(Math.atan2(x, y));
//System.out.println(angle);
return angle;
I have no clue why it works now but it didn't before as the procedure is basically identical...but hey it works! =D
Related
I'm working on something that involves clicking specific points on a buffered image in a JPanel. I had issues with this earlier in the project (affine transform translation not working properly), but nothing I found fixed it so I decided I would come back to it later.
I'm not entirely sure how to trouble shoot it since I'm a novice, but I think it's reading my y coordinates too low. I made a mouse input listener that tracks the number of times the user has clicked and gets the mouse pointer's location for functions I haven't made yet. For testing I have it output the coordinates and number of clicks then make a circle centered where the mouse clicks.
#Override
public void mouseClicked(MouseEvent e) {
Point mouseCursor = MouseInfo.getPointerInfo().getLocation();
panel.drawCenteredCircle(mouseCursor.getX(), mouseCursor.getY(), 100);
System.out.println(String.valueOf(mouseCursor));
System.out.println(String.valueOf(clickCount));
clickCount++;
}
Here is drawCenteredCircle in my custom panel class:
public void drawCenteredCircle(double x, double y, int r) {
imgG2 = image.createGraphics();
imgG2.setPaint(Color.RED);
x = (x-r/2.0);
y = (y-r/2.0);
imgG2.fillOval((int)Math.round(x), (int)Math.round(y), r, r);
this.repaint();
imgG2.dispose();
}
I tried taking a screenshot to show what happens, but the circle properly centers on the x coordinate, but not the y coordinate. Instead it draws the circle with the pointer at the top center edge.
I overrided the paintComponent of my JPanel to implement a zoom feature:
#Override
protected void paintComponent(Graphics g) {
//Implimenting zoom
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g.create();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
/*Supposed to counter the movement from the scale, not working properly
int imageWidth = image.getWidth();
int imageHeight = image.getHeight();
double x = (w - scale * imageWidth)/2;
double y = (h - scale * imageHeight)/2;*/
AffineTransform at = new AffineTransform()/*.getTranslateInstance(x, y) */;
at.scale(scale, scale);
g2.drawRenderedImage(image, at);
//g2.dispose(); I was told to put this, but I'm not sure if it's necessary or what it does entirely
}
My confused notes are because I got this code from an example someone made and, as I said earlier, the affine translation wasn't working (I took the actual translation out). They're irrelevant to the question.
The reason I put this is because I initially had code that was meant to fit the image to the screen/frame depending if it was fullscreen or not:
int x = image.getWidth();
int y = image.getHeight();
double frameW = frame.getBounds().getWidth();
double frameH = frame.getBounds().getHeight();
//Rectangle winSize = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
double screenW = Toolkit.getDefaultToolkit().getScreenSize().getWidth();
double screenH = Toolkit.getDefaultToolkit().getScreenSize().getHeight();
if (!isFullScreen) {
if (x/y > frameW/frameH) {
scale = frameW/x;
} else {
scale = frameH/y;
}
} else {
if (x/y > screenW/screenH) {
scale = screenW/x;
} else {
scale = screenH/y;
}
}
It uses my zoom function which scales the image with the double "scale." I noticed that when I zoomed in or out, it would change where the dots would appear relative to the pointer. It wasn't until I removed the code for the image to start fitted to the window and had it start at 100% that I received the result of the pointer being at the top center of the circle.
I also tried removing the part that's supposed to center the circle and the result was the pointer being on the left side and having a gap between it and the top of the circle.
Sorry if this is too much stuff. I'm pretty novice and learned just as much about java (the only coding language I know) working on this project as I knew when I first started it. I'm not sure what information I have that could be helpful in this, so I just threw in everything I thought could help. I appreciate any help, even irrelevant to my question.
I'm trying to build a Drawing Program using Processing. I am currently stuck on using PGrapchics.
When the user draws a rectangle, it shows the shape being drawn. When the user releases their mouse, it then creates a PGraphic of the final shape. I would then like the user to draw on top of that. Here is my problem:
I had to reset the background of the canvas when drawing a rectangle because otherwise, it shows a trail of rectangles. The result is that while the user draws a new rectangle the old ones disappear and come back once the mouse has been releasd
Some thoughts:
I would also like to add features where the user can select on a previously drawn rectangle and change it's colour, stroke, send to back, bring to front etc..
To achieve this, I'm storing all drawn rectangles (PGraphics) into an ArrayList which will be drawn via a for loop. This will allow me to adjust the behaviour by moving the PGraphics elements up and down the ArrayList.
PS: Instead of creating my own class of Shape am I better off using PShape?
int startX;
int startY;
int endX;
int endY;
boolean drawing;
int strokeW = 3;
Shape shape;
PGraphics shapeLayer;
ArrayList<PGraphics> layersList = new ArrayList();
void setup() {
size(500, 500);
cursor(CROSS);
background(255);
smooth();
}
void draw() {
strokeWeight(strokeW);
if (key >= '0' && key <= '9') {
strokeW = key - '0';
}
for(int i = 0; i < layersList.size(); i++) {
image(layersList.get(i), 0, 0);
}
if (drawing) {
shape.createRectangle();
}
}
void mousePressed() {
startX = mouseX;
startY = mouseY;
shapeLayer = createGraphics(width, height);
shapeLayer.beginDraw();
}
void mouseDragged() {
drawing = true;
endX = constrain(mouseX, 0, 500);
endY = constrain(mouseY, 0, 500);
shape = new Shape(startX, startY, endX, endY);
shapeLayer.clear();
}
void mouseReleased() {
drawing = false;
shapeLayer.endDraw();
layersList.add(shapeLayer);
}
Here is the Shape Class:
class Shape {
int startX;
int startY;
int endX;
int endY;
Shape(int x1, int y1, int x2, int y2) {
startX = x1;
startY = y1;
endX = x2;
endY = y2;
}
void createRectangle() {
background(255, 0);
shapeLayer.strokeWeight(strokeW);
shapeLayer.rectMode(CORNERS);
shapeLayer.rect(startX, startY, endX, endY);
rectMode(CORNERS);
rect(startX, startY, endX, endY);
}
}
In the future, please try to narrow your problem down to a MCVE before you post. For example you could have hard-coded it to draw a rectangle when the user drags instead of including all the code for every shape.
But your problem is caused by drawing to the screen and never clearing it out. You need to break your problem down into smaller pieces and then approach those pieces one at a time.
Step 1: Can you create a sketch that just shows a rectangle as you drag, but has the rectangle go away when you let go of the mouse? Start over with a basic sketch that does just this one thing, and get it working perfectly before you move on to the next step.
Step 2: Can you draw shapes to an off-screen buffer? It looks like you've tried this in your current code, but note that you never actually draw any shapes to your buffer, and you never actually draw your buffer to the screen. Again, start with a basic sketch that just does this. Don't even worry about user input or anything yet, just get a hard-coded rectangle drawn to an off-screen buffer, then draw that off-screen buffer to the screen.
Step 3: Can you combine those two to show the rectangle when you're drawing it, then draw it to the off-screen buffer when the user lets go?
Step 4: Only when you have the rectangle working perfectly, then move on to other shapes.
This is how programming works: you have to break your problem down into small steps like this, and then you have to approach each step in isolation. If you get stuck, you can come back with an MCVE showing just one of these steps, and we'll go from there. Good luck.
In addition to Kevin's answer: it does look like you are using another PGraphics buffer to draw into, but the whole sketch could be simpler.
Unless you need an undo/redo mode, where remembering the drawing commands and their order is needed, you can get away with something slightly simpler.
You can find a detailed answer with commented code showing something very similar. You simply need to add the pencil and line modes.
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.
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 am developing an application using Java2d. The weird thing I noticed is, the origin is at the top left corner and positive x goes right and positive y increases down.
Is there a way to move the origin bottom left?
Thank you.
You are going to need to do a Scale and a translate.
in your paintComponent method you could do this:
public void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
g2d.translate(0, -height);
g2d.scale(1.0, -1.0);
//draw your component with the new coordinates
//you may want to reset the transforms at the end to prevent
//other controls from making incorrect assumptions
g2d.scale(1.0, -1.0);
g2d.translate(0, height);
}
my Swing is a little rusty but this should accomplish the task.
We can use the following way to resolve easily the problem,
public void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
// Flip the sign of the coordinate system
g2d.translate(0.0, getHeight());
g2d.scale(1.0, -1.0);
......
}
Have you tried Graphics2D.translate()?
You're going to want to just get used to it. Like luke mentioned, you technically CAN apply a transform to the graphics instance, but that will end up affecting performance negatively.
Just doing a translate could move the position of 0,0 to the bottom left, but movement along the positive axes will still be right in the x direction and down in the y direction, so the only thing you would accomplish is drawing everything offscreen. You'd need to do a rotate to accomplish what you're asking, which would add the overhead of radian calculations to the transform matrix of the graphics instance. That is not a good tradeoff.
Just for later reference, I had to swap the order of the calls to scale and translate in my code. Maybe this will help someone in the future:
#Test
public void bottomLeftOriginTest() throws IOException {
int width = 256;
int height = 512;
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
Graphics2D ig = bi.createGraphics();
// save the "old" transform
AffineTransform old = ig.getTransform();
// origin is top left:
// update graphics object with the inverted y-transform
if (true) { /* order ok */
ig.scale(1.0, -1.0);
ig.translate(0, -bi.getHeight());
} else {
ig.translate(0, -bi.getHeight());
ig.scale(1.0, -1.0);
}
int xPoints[] = new int[] { 0, width, width };
int yPoints[] = new int[] { 0, height, 0 };
int nPoints = xPoints.length;
ig.setColor(Color.BLUE);
ig.fillRect(0, 0, bi.getWidth(), bi.getHeight());
ig.setColor(Color.RED);
ig.fillPolygon(xPoints, yPoints, nPoints);
// restore the old transform
ig.setTransform(old);
// Export the result to a file
ImageIO.write(bi, "PNG", new File("origin.png"));
}