Setting arc position in Java using Mouse - java

I am writing a 2D program. On my paintComponent I created an arc.
public class Board extends Panel{
protected void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D graphics2d = (Graphics2D)g;
int x = MouseInfo.getPointerInfo().getLocation().x;//set mouses current position
int y = MouseInfo.getPointerInfo().getLocation().y;
graphics2d.setStroke(wideStroke);
graphics2d.draw(new Arc2D.Double(200, 200, 100, 100, ?, 180, Arc2D.OPEN));
}
}
In my main I am using a Thread to update the graph. The position of the ? is the starting angle. Every time I change this the arc will move in a circle like half a car wheel. Is it possible to get the arc movement to follow the mouse? e.g. ? = 270
How will I do this? (Sorry for my bad paint skills!)

So based on the information from Java 2d rotation in direction mouse point
We need two things. We need the anchor point (which would be the centre point of the arc) and the target point, which would be the mouse point.
Using a MouseMotionListener, its possible to monitor the mouse movements within the component
// Reference to the last known position of the mouse...
private Point mousePoint;
//....
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
mousePoint = e.getPoint();
repaint();
}
});
Next we need to calculate the angle between these two points...
if (mousePoint != null) {
// This represents the anchor point, in this case,
// the centre of the component...
int x = width / 2;
int y = height / 2;
// This is the difference between the anchor point
// and the mouse. Its important that this is done
// within the local coordinate space of the component,
// this means either the MouseMotionListener needs to
// be registered to the component itself (preferably)
// or the mouse coordinates need to be converted into
// local coordinate space
int deltaX = mousePoint.x - x;
int deltaY = mousePoint.y - y;
// Calculate the angle...
// This is our "0" or start angle..
rotation = -Math.atan2(deltaX, deltaY);
rotation = Math.toDegrees(rotation) + 180;
}
From here, you would need to subtract 90 degrees, which would give your arcs start angle and then use an extent of 180 degrees.

Related

LibGDX: Sprite.setBounds doesn't work correctly with volatile coordinates and InputAdapter

Faced a problem while trying to handle clicking on a moving image.
I used InputAdapter.touchDown() to handle the click and created Sprite for the image. Then I set the borders through Sprite.setBounds(). Further, in fact, the problem: if the coordinates in setBounds() are unchanged - the click is handled correctly. But if you change them (position.x++, for example) - the object comes into motion, but clicks are not read.
I can’t understand where the reason.
I tried to make a alterable variable outside the method, but this also didn't bring any result.
I tried using batch.draw(img) instead of img.draw(batch) - the effect is the same.
I tried to relocate Gdx.input.setInputProcessor() to the render() method, after img.setBounds() - nothing changed.
I even compared the coordinates of the Img and the Bounds area online, in motion - they are the same, as it should be.
Img and handler in constructor:
img = new Sprite(new Texture(finSize));
centerX = Gdx.graphics.getWidth()/2-img.getWidth()/2;
centerY = Gdx.graphics.getHeight()/2-img.getHeight()/2;
startPosition = new Vector2(centerX, centerY);
Gdx.input.setInputProcessor(new InputAdapter(){
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
if(img.getBoundingRectangle().contains(screenX, screenY))
System.out.println("Image Clicked");
return true;
}
});
Render:
public void render(SpriteBatch batch, float radius, float boost) {
speed+=boost;
nextX = radius * (float) Math.cos(speed); // Offset step X
nextY = radius * (float) Math.sin(speed); // Offset step Y
// Img is moving, but clicks is not handling
img.setBounds(startPosition.x+ nextX, startPosition.y + nextY, 100, 100);
// Handling clicks fine, but img is motionless
img.setBounds(startPosition.x, startPosition.y, 100, 100);
img.draw(batch);
// Checking coordinates - all's fine
System.out.println(img.getBoundingRectangle().getX());
System.out.println(startPosition.x + nextX);
System.out.println(img.getBoundingRectangle().getY());
System.out.println(startPosition.y + nextY);
}
So, I sequentially compared the XY coordinates of the image and the mouse click point and came to the conclusion that InputAdaper and Sprite consider Y differently - from above and from below. Therefore, X always coincided, and Y had a big difference in values.
As a result, I entered two corrected coordinates xPos \ yPos (Y subtracted from the total field height) for the center of the pic and in the touchDown() method, instead of comparing with BoundRectangle, simply compared the difference in the coordinates of the pic and the click modulo. If the result into the image size range - everything is ok.
Now clicks on the moving image works correctly.
public void render(SpriteBatch batch, float radius, float boost) {
speed+=boost; // rotational speed
nextX = radius * (float) Math.cos(speed); // Offset step X
nextY = radius * (float) Math.sin(speed); // Offset step Y
// set image size and position
img.setBounds(startPosition.x+nextX, startPosition.y+nextY, 100, 100);
img.draw(batch);
// Corrected coordinates of the image for InputAdapter coordinate system
xPos = img.getX()+img.getWidth()/2;
yPos = Gdx.graphics.getHeight()-img.getY()- img.getHeight()/2;
// Check the coincidence of the coordinates of the image area and the click point
Gdx.input.setInputProcessor(new InputAdapter(){
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
if((Math.abs(xPos-screenX)<=img.getWidth()) && (Math.abs(yPos-screenY)<=img.getHeight()))
{System.out.println("Image Clicked");}
return true;
}
});
}

Dragging rotated text inside android canvas does not work as expected

There is something I am missing inhere so I hope you can share some light on me.
I am drawing some text inside canvas. For this I have a class Word
public class Word {
private int x;
private int y;
private String text;
}
The app allows the user to rotate the text, and I handle the rotation withing onDraw
protected void onDraw(Canvas canvas) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.rotate(angle, centerX, centerY)
...
canvas.drawText(word.getText(), word.getX(), word.getY())
....
canvas.restore();
}
The problem I get is when the user drags the canvas and there is a rotation set. When angle=0 the movement is going as expected.
#Override
public boolean onTouchEvent(MotionEvent event) {
case MotionEvent.ACTION_DOWN:
initialX = (int) event.getX();
initialY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
int currentX = (int) event.getX();
int currentY = (int) event.getY();
int xMovement = currentX - initialX;
int yMovement = currentY - initialY;
dragWords(xMovement, yMovement);
.....
and on dragWords for each word I do:
private void dragText(int xMovement, int yMovement){
for (Word word : words) {
word.setX(word.getX() + xMovement);
word.setY(word.getY() + yMovement);
}
invalidate();
}
When rotation angle is 0, moving up/down/left/right makes the words move by the same distance. As angle gets bigger, the words start to move in different dirrections, for instance at 60, it is starting to go diagonally up, when 180 it only moves up/down and not left/right.
I think I need to calculate some sort of a difference based on angle and add it to xMovement/yMovement... but how should I do this ?
LE: Here is an image on how it behaves:
The blue lines is how the text is moving on drag while the orange is the finger dragging on the screen. When angle is 0 it works quite well, when angle increases, it starts to move diagonally on left/right, while when angle is even bigger, it only moves up and down and does not respond to left/right
If I understand correctly, the issue is that Canvas.rotate() does not only rotate the text direction, but rather the whole canvas. Therefore, the x-y coordinates of the words are also rotated from the specified pivot point.
In order to match the dragging movement, you can use a Matrix for this, more specifically the inverse matrix of the one you're using to rotate the canvas. It will be used to convert the x-y coordinates of the words to their original, pre-rotate locations.
For example, calculate this once, and update it whenever angle, centerX, or centerY changes.
// rotMatrix is the same operation applied on the canvas.
Matrix rotMatrix = new Matrix();
rotMatrix.postRotate(mAngle, centerX, centerY);
// Invert it to convert x, y to their transformed positions.
Matrix matrix = new Matrix();
rotMatrix.invert(matrix);
Then, when drawing each word:
int wordX = ...
int wordY = ...
String text = ...
float[] coords = new float[] { wordX, wordY };
matrix.mapPoints(coords);
canvas.drawText(text, coords[0], coords[1], paint);
In the ellipses part in the following code:
dragWords(xMovement, yMovement);
..... <<<--------------------- I hope you are updating initialX and initialY
initialX = currentX;
initialY = currentY;
Otherwise, your x and y values will not correspond correctly with the amount of distance moved during the touch gesture.
As user matiash indicated, you should use Matrix#mapPoints(float[]) to transform your x and y values. Declare and initialize a Matrix:
Matrix correctionMatrix;
// Your view's constructor
public MyView() {
....
correctionMatrix = new Matrix();
}
Here's how your onDraw(Canvas) should look like:
#Override
protected void onDraw(Canvas canvas) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.rotate(angle, centerX, centerY);
...
// Neutralize the rotation
correctionMatrix.setRotate(-angle, centerX, centerY);
// Initialize a float array that holds the original coordinates
float[] src = {word.getX(), word.getY()};
// Load transformed values into `src` array
correctionMatrix.mapPoints(src);
// `src[0]` and `src[1]` hold the transformed `X` and `Y` coordinates
canvas.drawText(word.text, src[0], src[1], somePaint);
....
canvas.restore();
}
This should give you the desired results - movement in the X and Y axis irrespective of canvas rotation.
You can obviously move the call to setRotate(float, float, float) to a better place. You only need to call it once after changing the angle value.

simple Circle on circle Collision libgdx

made two circles one of radius 8(image 16x16)
and one of radius 20( image 40x40)
i am calling the circle over overlap method and the collsion is just off. It is colliding with a circle that is around the 0,0 point of where ever my image of the ball is. the bullet can go within the ball on the bottom and right sides.
public class MyGame extends ApplicationAdapter {
SpriteBatch batch;
Texture ballImage, bulletImage;
OrthographicCamera cam;
Circle ball;
Array <Circle> bullets;
long lastShot;
#Override
public void create ()
{
System.out.println("game created");
ballImage = new Texture(Gdx.files.internal("ball.png"));
bulletImage = new Texture(Gdx.files.internal("bullet.png"));
cam = new OrthographicCamera();
cam.setToOrtho(true,320,480);//true starts top right false starts top left
batch = new SpriteBatch();
ball = new Circle();
ball.radius=20;
ball.x=320/2-ball.radius; // half screen size - half image
ball.y=480/2-ball.radius;
bullets = new Array<Circle>();
spawnBullet();
/*
batch.draw(bulletImage,bullet.x,bullet.y);
bullet.x++;
bullet.y++; */
}
public void spawnBullet()
{
Circle bullet = new Circle();
bullet.radius=8;
bullet.x=0;
bullet.y=0;
bullets.add(bullet);
lastShot = TimeUtils.nanoTime();
}
#Override
public void render ()
{
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
cam.update();
batch.setProjectionMatrix(cam.combined);
batch.begin();
batch.draw(ballImage,ball.x,ball.y);
for(Circle bullet: bullets)
{
batch.draw(bulletImage, bullet.x, bullet.y);
}
batch.end();
if(Gdx.input.isTouched())
{
Vector3 pos = new Vector3();
pos.set(Gdx.input.getX(), Gdx.input.getY(),0);
cam.unproject(pos);
ball.y = pos.y - ball.radius;
ball.x = pos.x - ball.radius ;
}
//if(TimeUtils.nanoTime()-lastShot >1000000000) one second
//spawnBullet();
Iterator<Circle> i = bullets.iterator();
while(i.hasNext())
{
Circle bullet = i.next();
bullet.x++;
bullet.y++;
if(bullet.overlaps(ball))
{
System.out.println("overlap");
i.remove();
}
}
}
}
If your bullet and the ball are 2 circles, like you said you don't need an overlap method.
It is simple: 2 circles collide, if their distance is smaller then the sum of their radiuses.
To calculate the distance you need to make a squareroot. This is a pretty expensive calculation, so it would be better to use squared distance and squared sum of radiuses:
float xD = ball.x - bullet.x; // delta x
float yD = ball.y - bullet.y; // delta y
float sqDist = xD * xD + yD * yD; // square distance
boolean collision = sqDist <= (ball.radius+bullet.radius) * (ball.radius+bullet.radius);
Thats it.
Also in your cam.setToOrtho you wrote a cooment:
//true starts top right false starts top left
Thats wrong, it is top left or bottom left. By default it is bottom left, because this is the way a coordinate system works normaly. The top left is, because the monitor addresses pixels starting from top left = pixel 1.
EDIT: this should be the problem: The coordinates you give the batch.draw method are the left lower corner of the Texture by default, if you are using the "y = Down"-System it should be the top left corner (you have to try i am not sure).
The Circles position instead is its center.
To solve the problem you need to adjust the position like this (for "y = Up"-System):
batch.draw(bulletImage, bullet.x - bullet.radius, bullet.y - bullet.radius);
It is possible, that the same formula works also for the "y = Down"-System but i am not sure

AffineTransform() rotates rectangle way too fast

I am trying to draw a rectangle with java awt and make it rotate with mouse cursor by mouse dragging.
When I was testing it out, the rectangle was rotating ridiculously fast.
My Rectangle():
private Rectangle2D rec = new Rectangle2D.Float(x0,y0,w,h);
AffineTransform recTrans = new AffineTransform();
int pivotX = x0+w/2, pivotY = y0+h;
// (0,0) is at the top-left corner
My paintComponent():
public void paintComponent(Graphics g) {
Graphics2D graph = (Graphics2D) g;
graph.translate(x,y);
graph.transform(recTrans);
graph.fill(rec);
graph.setColor(Color.blue);
graph.draw(rec);
}
My mouse dragging event:
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
previousX = e.getX();
previousY = e.getY();
}
});
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
currentX = e.getX();
currentY = e.getY();
double angle1 = Math.atan2(currentY-pivotY, currentX-pivotX);
double angle2 = Math.atan2(previousY-pivotY, previousX-pivotX);
double theta = angle2 - angle1;
recTrans.rotate(theta, pivotX, pivotY);
}
});
So supposedly the scenario looks like:
But when I slightly drag(theta less than 10 degree) the rectangle to the right side, the rectangle rotates even to the bottom of the pivot point.
Another note, the rectangle rotates but the coordinates of the four corners of the rectangle yet have changed.
I am quite lost when doing those transformation tasks with java..
I think that the problem is that you are accumulating the rotation for each mouse motion event you get: when you move the mouse 10 degrees you don't get just one event at the end of the movement, you get a bunch of events all along the way. And it seems that you are accumulating the rotation of all of them in the same transformation, so when you get to 10 degrees, you have actually 1 + 2 + 3 + 4... you get the idea.
The solution is easy: reset the transformation before applying the rotation:
recTrans.setToIdentity();
recTrans.rotate(theta, pivotX, pivotY);
Or equivalently but nicer, use this function that does not accumulate the transformation.
recTrans.setToRotation(theta, pivotX, pivotY);
recTrans.rotate(Math.toRadians(theta), pivotX, pivotY);
This will help.

Drawing rectangles at an angle

What is a method in Java that draws a rectangle given the following:
The coordinates of the center of the square
The angle of the rectangle from vertical, in degrees
To draw a rectangle in the way you suggest you need to use the class AffineTransform. The class can be used to transform a shape in all manner of ways. To perform a rotation use:
int x = 200;
int y = 100;
int width = 50;
int height = 30;
double theta = Math.toRadians(45);
// create rect centred on the point we want to rotate it about
Rectangle2D rect = new Rectangle2D.Double(-width/2., -height/2., width, height);
AffineTransform transform = new AffineTransform();
transform.rotate(theta);
transform.translate(x, y);
// it's been while, you might have to perform the rotation and translate in the
// opposite order
Shape rotatedRect = transform.createTransformedShape(rect);
Graphics2D graphics = ...; // get it from whatever you're drawing to
graphics.draw(rotatedRect);
For the first point, you can just figure out the coordinates of the center of the square by using a distance formula, (int)Math.sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2)); them divide by 2. you can do this for the width and height. I don't know enough about Java draw to give you better answers based on what was in your question but I hope that helps.
For the second, you would need to just create a polygon right?

Categories

Resources