How to set rotation of a view based on other view position - java

I know the question doesn't make a lot of sense, but I have two views.
The first one I set its position X & Y based on touch event on the activity.
The second one is like an arrow fixed in the middle of the activity and I want it to point exactly where the the first view is positioned.
so is there a way that can convert a view's position coordinates into a angle value based from the center of the screen.?
parentView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent event) {
marker.setX(event.getX()); // marker is the first view
marker.setY(event.getY());
FixedArrow.setRotation(/* ??? */); //FixedArrow is the second view
return true;
}
});

It's task from geometry from middle school. Basically what you need is to solve right triangle. One side of this triangle is always a line from the centre of the screen to the nearest screen border, another one from centre to the point of user click. Applying there trigonometry, you can calculate an angle.
Final formula and explanation with picture for understanding can be found in this answer

try something like this
public float getAngle(Point target, Point center) {
float angle = (float) Math.toDegrees(Math.atan2(target.y - center.y, target.x - center.x));
if(angle < 0){
angle += 360;
}
return angle;
}
to call it do something like this
Point target = new Point((int) event.getX(), (int) event.getY());
Point center = new Point(parentView.getWidth()/2, parentView.getHeight()/2);
FixedArrow.setRotation(getAngle(target,center));

Related

Using XY coordinates get the height of coordinates in the view - Android

After I touch area in my view, I want to use the XY coordinates to determine where in the view the height of that touch is. So lets say I have a view that is 700 pixels tall, and I touch area in the middle I want to be able to determine the height of the touch in pixel. So for example I touch the middle of the view, I want to return that touched was recorded at 350 pixels high out of the 700 pixels. Is that possible?
myView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
float y = event.getY();
float x = event.getX();
//Using the xy coordinates I want the height of the touch in the view
return false;
}
});
As per the official documentation, these methods are returning exactly what you are asking for already (the x-coordinate of the click relative to the View), whereas getRawX() would instead return the x-coordinate relative to the screen.

How to zoom into mouse position with correct translation

I'm trying to zoom a grid in Processing and I am having trouble with applying the correct translation such that zooming is centered around the mouse position. I have searched the web for a while but nothing I try seems to work.
The screen size is width and height and the mouse position is mouseX and mouseY.
The code I have at the moment is below, but it zooms the grid (controlled by player.zoom) from the top left corner which is not what I want. To update the translation of the grid, player has the 2d vector player.translate.
void mouseWheel(MouseEvent event) {
float zoomFactor = 200.0f;
float scroll = event.getCount();
player.zoom -= scroll * player.zoom / zoomFactor;
// player.translate.x += something;
// player.translate.y += something;
}
If you need more details to answer I can link the repo with the source code.
I have created a very simple mock-up for you which will hopefully point you in the right direction into applying this to your player.
So this little demo shows the zooming in to the centre of an ellipse whilst keeping it as the central focus.
float scale = 1;
// displacement left/right
float xPan = 720;
// displacement up/down
float yPan = 450;
boolean zoomIn = true;
void setup() {
size(1440, 900);
}
void draw() {
// allows us to zoom into the center of the screen rather than the corner
translate(width/2, height/2);
scale(scale);
translate(-xPan, -yPan);
background(200);
// draws the ellipse in the center
ellipse(width/2, height/2, 100, 100);
// does the zooming
if (zoomIn) {
scale *= 1.01;
}
}
I suggest you to copy this into a new project and then comment out specific lines to try to understand what's going on and how you can transfer this over to your own project.
The same principles still apply, I didn't do this with mouse input in the effort of making the program as simple as possible.

Zoom in on point in canvas, where everything is based off of a certain point

Alright, bit of a strange one. I'll do my best to explain.
I'm building a game on Android (Java). And I'm just working out the views first. The first thing I'm working on is a solar system view (top down, 2d, no tilt). I've got all the planets and the star displaying properly, and in proportion. I can pan, and zoom. The only issue is, the way I've set up all the circles to draw, they're all based on one point (where the star is), and when I 'zoom'/'pan' I'm not actually zooming/panning. For the pan, I'm just moving the point where everything revolves, and for the zoom, I'm not scaling the whole canvas (tried that, didn't work out) I'm adjusting the size of the elements I'm drawing, in accordance with the zoom.
So I've got a 'star' that starts out in the middle of the screen, the point is called orbitLocation.
And a zoom variable that starts at 1f.
The star is drawn very simply - canvas.drawCircle((float)orbitLocation.x, (float)orbitLocation.y, this.radius * zoom, this.paint);
Each of the planets are positioned like so:
canvas.drawCircle(getPlanetX(angle, orbitLocation, zoom), getPlanetY(angle, orbitLocation, zoom), this.radius * zoom, this.paint);
getPlanetX and getPlanetY look like this:
private float getPlanetX(double angle, Point orbitingPoint, float zoom) {
return orbitingPoint.x + (this.distance * Constants.AU * zoom) * (float)Math.sin(angle);
}
private float getPlanetY(double angle, Point orbitingPoint, float zoom) {
return orbitingPoint.y + (this.distance * Constants.AU * zoom) * (float)Math.cos(angle);
}
Constants.AU is a constant int 11507 in place for Astronomical Unit - for proper scaling.
Now the problem I'm having with zoom, is if I pan, and then zoom, the whole thing scales from the orbitLocation, disregarding where I'm trying to zoom in on, so whatever was right in the middle of my screen, quickly gets thrown way off screen.
Desired outcome. Have whatever is in the center of the screen (or between fingers in pinch, either way), stay in the center of the screen, and the orbit point adjust itself in accordance.
Here's the relevant part of my touch event:
if (touchCount == 2) { // if pinch
if (startZoomTouchPoint != null) {
float newDistance = getDistance(event, 0, 1);
float distanceDifference = (startZoomDistance - newDistance);
zoom -= distanceDifference * (zoom / 100);
if (zoom < MIN_ZOOM) {
zoom = MIN_ZOOM;
}
// Point zoomCenter = getZoomCenter(event);
// int yOrbitOffset = orbitLocation.y - zoomCenter.y;
// int xOrbitOffset = orbitLocation.x - zoomCenter.x;
// orbitLocation.x += xOrbitOffset + Constants.AU * zoom;
// orbitLocation.y += yOrbitOffset + Constants.AU * zoom;
startZoomDistance = newDistance;
updateStartZoomTouchPoint(event);
}
}
Those commented lines in the middle are my latest attempt to achieve my goal.
Every time I try, I either move the orbitLocation way too much, or not enough, or it moves around weirdly.
getZoomCenter literally just gets the point between the 2 fingers. We can just use the center of the screen if that's easier.
Alright, that's all I've got. Let me know if you need any more info.
Please look at the following code snippet
private ScaleGestureDetector mScaleDetector;
private float zoom = 1.f;
public MyCustomView(Context mContext){
...
// View code goes here
...
mScaleDetector = new ScaleGestureDetector(context, new
ScaleListener());
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(ev);
return true;
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.scale(zoom, zoom);
...
// onDraw() code goes here
...
canvas.restore();
}
private class ScaleListener
extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
zoom = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
invalidate();
return true;
}
}
Basically, you need to scale the canvas based on the scale value obtained from the touch event. You can do that by
Save the canvas
Scale the canvas
Draw what you were drawing
Restore the canvas
and you can set the zoom on the response of scale gesture detector. Call invalidate afterwards.
For more information visit https://developer.android.com/training/gestures/scale

LibGDX touch coordination confusion

as I understood, libGDX coordinate system set by default, so the (0,0) is located at the bottom left corner, like in the following picture:
(source: gyazo.com)
Is it possible to make it work like JFrame, where the default position is at the top left corner?
Anyhow, back to my actual question, android (OR at least my phone) touch coordinate system is working by full screen (when phone is laying on it's side) so the default (0, 0) position is located at the top right corner, like in the following picture:
(source: gyazo.com)
So what happens when I simply do this:
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
System.out.println("X: " + screenX + " Y: " + screenY);
this.instance.player.setLocation(screenX, screenY);
return false;
}
I simply get the X, Y of touched screen (based on android's fullscreen coordinate system) and then use it on libgdx coordinate system which is totally different,
so if I touched at the top right corner, my blue rectangle will appear at bottom left corner.
Is there a way to make everything work by the top left corner like on PC?
You'll need to use a camera to do this. If you're not using a camera, I'd highly recommend adding one in, as it makes things a lot easier. Simply call camera.setToOrtho(true), which will switch LibGDX's coordinate system to the one you want, (0,0) in the top left.
To do this with a certain viewport width and height other than what you get from Gdx.graphics.getWidth/Height(), you can call camera.setToOrtho(true, viewportWidth, viewportHeight).
I don't know how to set it at top left corner but one thing you could is to convert input coordinate to libgdx coordinate.
I hope that could help you or help somebody else :
int Screen_height = Gdx.graphics.getHeight(); // Your screen height
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
System.out.println("X: " + screenX + " Y: " + (Screen_height - screenY));
this.instance.player.setLocation(screenX, (Screen_height - screenY));
return false;
}
Yes, and it's very easy.
You just have to subtract from the Y value the height of the screen.
So:
The finger position Y = Heigh of the screen - the Y value you have found.
There's an easier way:
Just make the subtraction bellow
Gdx.graphics.getHeight()-screenY
Only this line of code is necessary, as you can see.
Create the variable fingerY and use the subtraction:
fingerY= Gdx.graphics.getHeight()-screenY;
So, if screenY equals 10 from the left bottom corner and the height of the screen equals 240 pixels:
fingerY=240-10
Then fingerY=230 pixels from the bottom left corner, while it is 10 pixels from the top left corner.
You need to use the method project(Vector3 worldCoords) in class com.badlogic.gdx.graphics.Camera.
private Camera camera;
............
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
Create an instance of the vector and initialize it with the coordinates of the input event handler.
Vector3 worldCoors = new Vector3(screenX, screenY, 0);
Projects the worldCoors given in world space to screen coordinates.
camera.project(worldCoors);
Use projected coordinates.
world.hitPoint((int) worldCoors.x, (int) worldCoors.y);
OnTouch();
return true;
}

libgdx coordinate system differences between rendering and touch input

I have a screen (BaseScreen implements the Screen interface) that renders a PNG image. On click of the screen, it moves the character to the position touched (for testing purposes).
public class DrawingSpriteScreen extends BaseScreen {
private Texture _sourceTexture = null;
float x = 0, y = 0;
#Override
public void create() {
_sourceTexture = new Texture(Gdx.files.internal("data/character.png"));
}
.
.
}
During rendering of the screen, if the user touched the screen, I grab the coordinates of the touch, and then use these to render the character image.
#Override
public void render(float delta) {
if (Gdx.input.justTouched()) {
x = Gdx.input.getX();
y = Gdx.input.getY();
}
super.getGame().batch.draw(_sourceTexture, x, y);
}
The issue is the coordinates for drawing the image start from the bottom left position (as noted in the LibGDX Wiki) and the coordinates for the touch input starts from the upper left corner. So the issue I'm having is that I click on the bottom right, it moves the image to the top right. My coordinates may be X 675 Y 13, which on touch would be near the top of the screen. But the character shows at the bottom, since the coordinates start from the bottom left.
Why is what? Why are the coordinate systems reversed? Am I using the wrong objects to determine this?
To detect collision I use camera.unproject(vector3). I set vector3 as:
x = Gdx.input.getX();
y = Gdx.input.getY();
z=0;
Now I pass this vector in camera.unproject(vector3). Use x and y of this vector to draw your character.
You're doing it right. Libgdx generally provides coordinate systems in their "native" format (in this case the native touch screen coordinates, and the default OpenGL coordinates). This doesn't create any consistency but it does mean the library doesn't have to get in between you and everything else. Most OpenGL games use a camera that maps relatively arbitrary "world" coordinates onto the screen, so the world/game coordinates are often very different from screen coordinates (so consistency is impossible). See Changing the Coordinate System in LibGDX (Java)
There are two ways you can work around this. One is transform your touch coordinates. The other is to use a different camera (a different projection).
To fix the touch coordinates, just subtract the y from the screen height. That's a bit of a hack. More generally you want to "unproject" from the screen into the world (see the
Camera.unproject() variations). This is probably the easiest.
Alternatively, to fix the camera see "Changing the Coordinate System in LibGDX (Java)", or this post on the libgdx forum. Basically you define a custom camera, and then set the SpriteBatch to use that instead of the default.:
// Create a full-screen camera:
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
// Set it to an orthographic projection with "y down" (the first boolean parameter)
camera.setToOrtho(true, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera.update();
// Create a full screen sprite renderer and use the above camera
batch = new SpriteBatch(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
batch.setProjectionMatrix(camera.combined);
While fixing the camera works, it is "swimming upstream" a bit. You'll run into other renderers (ShapeRenderer, the font renderers, etc) that will also default to the "wrong" camera and need to be fixed up.
I had same problem , i simply did this.
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
screenY = (int) (gheight - screenY);
return true;
}
and every time you want to take input from user dont use Gdx.input.getY();
instead use (Gdx.graphics.getHeight()-Gdx.input.getY())
that worked for me.
The link below discusses this problem.
Projects the given coords in world space to screen coordinates.
You need to use the method project(Vector3 worldCoords) in class com.badlogic.gdx.graphics.Camera.
private Camera camera;
............
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
Create an instance of the vector and initialize it with the coordinates of the input event handler.
Vector3 worldCoors = new Vector3(screenX, screenY, 0);
Projects the worldCoors given in world space to screen coordinates.
camera.project(worldCoors);
Use projected coordinates.
world.hitPoint((int) worldCoors.x, (int) worldCoors.y);
OnTouch();
return true;
}

Categories