Detecting touch events on shapes - android

I've got this graph-like circle that is divided in several "portions". I draw these using drawArc on a canvas.
canvas.drawArc(oval, startAngle, sweepAngle, true, fillPaint);
I would like to make these arcs clickeable so I can trigger events when the user taps them. So I decided to override onTouchEvent, then for ACTION_DOWN go through all my portions and check if the touch position lies within them.
I tried this code here which is answering to a similar question, but I am getting strange angle values (i.e. negative values for top right quadrant, values between 180-270 in the rest of them).
public boolean isInArea(float x, float y){
int deltaX = (int) (x - oval.centerX());
int deltaY = (int) (y - oval.centerY());
if (Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)) > mRectShape.width()/2) {
return false;
}
// offset for radiant, depending on quadrant
double offset = 0;
if (deltaX > 0 && deltaY < 0) {
// top right -> nothing to do
} else if (deltaX > 0 && deltaY > 0) {
// bottom right -> add 90 degrees
offset = Math.PI/2;
} else if (deltaX < 0 && deltaY > 0) {
// bottom left -> add 180 degrees
offset = Math.PI;
} else if (deltaX < 0 && deltaY < 0) {
// top left -> add 270 degrees
offset = Math.PI * 3/2;
}
//now calculate angle
double angle = Math.asin(deltaY / deltaX);
double total = angle + offset;
double totalDeg = total * 180 / Math.PI;
Log.d(LOG_TAG, "angle :" + totalDeg);
if(totalDeg > mStartAngle && totalDeg < mSweepAngle){
return true;
}
return false;
}
I also tried without setting the offsets nand then I get negative values for top quadrants, and positive values between 0 and 180 in the bottom ones.
Also tried using atan2 like this:
angle = Math.atan2(deltaY, -deltaX);
What am I missing here?

Related

Rotate sprite in libgdx by following the position of finger on android

I'm trying to rotate a sprite based upon where my finger is placed on the touch screen.
I have an arrow and i want the arrow to be pointing at my finger at all times as i'm drag it around over the screen.
I could easily set the sprite to any rotation using sprite.setRotation(angle)
How should i go about this?
Much appreciated if you can point me in the right direction.
(ax, ay) is coordinates of the center of the arrow, and (fx, fy) is the coordinates of the finger, and a is the angle, here is some pseudocode:
dx = fx - ax
dy = fy - ay
if (dx == 0) {
a = 90
return
}
if (dy == 0) {
a = 0
return
}
//tan(a) == abs(dy) / abs(dx) therefore
a = arctan(dy / dx)
if (dx > 0 && dy > 0) {
// do nothing, a is correct
} else if (dx > 0 && dy < 0) {
a = 360 - a
} else if (dx < 0 && dy > 0) {
a = 180 - a
} else {
a = 180 + a
}
I didn't implement and test it yet, I'll do it later if there will be need

Animating a list of points to move away from a given touch location

The animation I am working on achieving is shown below in Figure 1.
1)I have a list containing points on a 2D plane(left) that I am working on animating.
2)A touch is made at location x, and an invisible circle is drawn (middle). I want all of the points contained in that circle to move away from the center (x).
3) A final result example (right)
I am planning on applying this in a way that I can supply any list of points, touch location, numFrames, and force applied on each point per frame. An array of length numFrames is returned, each array item being an already animated list.
Since I plan on implementing this on screen touch, it is possible for another touch to happen while the first touch animation is already in progress. How can I handle these collisions?
Figure 1:
here's my 2 cents, you need a list of points, these points should have a position, a velocity and a method to update the position. something like this
public class Point {
public float x, y;
public boolean isMoving;
private float dx,dy;
public Point(float x, float y){
this.x = x;
this.y = y;
}
public boolean move(float maxWidth, float maxHeight){
if(!isMoving) return false;
// update the position
x += dx;
y += dy;
dx /= FRICTION;
dy /= FRICTION;
// bounce...?
if(x < 0 || x > maxWidth){
dx *= -1;
}
if(y < 0 || y > maxHeight){
dy *= -1;
}
if(Math.abs(dx) < MOVEMENT_THRESHOLD && Math.abs(dy) < MOVEMENT_THRESHOLD){
isMoving = false;
}
return isMoving;
}
}
on every touch event you apply a force to every point within the radius and set their velocity
for(Point point : mPoints){
float distance = point.distance(x,y);
if(distance > mTouchRange) continue;
float force = (float) Math.pow(1 - (distance / mTouchRange), 2) * mForceFactor;
float angle = point.angle(x,y);
point.dx -= Math.cos(angle) * force;
point.dy -= Math.sin(angle) * force;
point.isMoving = true;
}
then you need an animation that call move on every frame and eventully stops when there are no moving points
you can find the full example here

How to write an intersects for Shapes in android

I have an written an Object called Shape which has a Point representing the topLeftCorner and a Dimension with represents its width and height.
To get the topRightCorner I can simply add the width to the topLeftPoint.x. I use to rotate them on a certain degree around their center. The problem after the rotation is, that my intersects(Shape) method fails, because it does not honor the rotation of the Shapes. The rotation will be the same for each Shape. My current implementation looks like this inside my Shape Object:
public boolean intersects(Shape s){
// functions returning a Point of shape s
return intersects(s.topLeft())
|| intersects(s.topRight())
|| intersects(s.bottomLeft())
|| intersects(s.bottomRight())
|| intersects(s.leftCenter())
|| intersects(s.rightCenter())
|| intersects(s.center());
}
public boolean intersects(Point p){
return p.x >= leftX()
&& p.x <= rightX()
&& p.y >= topY()
&& p.y <= bottomY();
}
Basically I need functions like rotatedLeftX() or rotatedTopRight() to work properly. Also for that calculation I think it doesn't matter when the topLeft point before a rotation of ie 90 will turn into topRight...
I already read this and this Question here, but do not understand it fully.
I modified an algorithm from Stackoverflow to do what you are indicating with rectangles for a battleship game I wrote. Here is the code:
private boolean overlaid(int [][][] shps, int curr)
{
for (int i = curr-1; i>=0; --i)
{
// From: http://stackoverflow.com/questions/306316/determine-if-two-rectangles-overlap-each-other/306332#306332
// if (RectA.X1 < RectB.X2 && RectA.X2 > RectB.X1 &&
// RectA.Y1 < RectB.Y2 && RectA.Y2 > RectB.Y1)
if (shps[curr][0][1] <= shps[i][1][1] &&
shps[curr][1][1] >= shps[i][0][1] &&
shps[curr][0][0] <= shps[i][1][0] &&
shps[curr][1][0] >= shps[i][0][0])
return true;
}
return false;
}
private int [][][] shps = { {{-1,-1},{-1,-1}},
{{-1,-1},{-1,-1}},
{{-1,-1},{-1,-1}} };
The shps parameter is just a matrix indicating the location of the top-left and bottom-right corners of each rectangle using {x0,y0}, {x1,y1}. For example, shps[curr][0][1] == the current shp's y0. For my purposes, I had to use <= and >=. Also, you have to be mindful of the reverse of y if you are using screen coordinates versus Cartesian coordinates. Also DeMorgan's Law if you want to use NOT overlaid.
I have a solution:
lets assume I want to calculate the rotation (90 degrees) of a Shape with x =1, y=1 (topLeft Point) and a width of 4 and a height of 6 around its center (3, 4) == (x1, y2)
rotatedX = x1 + cos(q) * (x - x1) - sin(q) * (y - y1)
rotatedY = y1 + sin(q) * (x - x1) + cos(q) * (y - y1)
in this case:
rotatedX = 3 + cos(90) * (1 - 3) - sin(90) * (1 - 4)
rotatedY = 4 + sin(90) * (1 - 3) + cos(90) * (1 - 4)
this is for a rotation in a cartesian-plane (where positive rotation values means rotation counter-clockwise)
So if you wanna aply a rotation of 90 degrees CLOCKWISE you just have to multiply rotation with -1;

Android touch direction northwest, northeast for UP direction

Sorry if the title sounds confusing - but this is what I am trying to do:
I have a large circular button on which I detect touch direction. I am able to find the UP/DOWN/LEFT/RIGHT from the dy and dx of the change in touch input coordinates like this:
if(Math.abs(dX) > Math.abs(dY)) {
if(dX>0) direction = 1; //left
else direction = 2; //right
} else {
if(dY>0) direction = 3; //up
else direction = 4; //down
}
But now I would like to handle cases where the button can be slightly rotated and thus the touch direction will also need to adjust for this. For example, if the button is rotated slightly to the left, then UP is now the finger moving northwest instead of just pure north. How do I handle this?
Use Math.atan2(dy, dx) to get the angle anticlockwise from the positive horizontal of the coordinate in radians
double pressed = Math.atan2(dY, dX);
subtract the rotation amount (anticlockwise rotation amount in radians) from this angle, putting the angle into the coordinate system of the button
pressed -= buttonRotation;
or if you have your angle in degrees, convert it to radians
pressed -= Math.toRadians(buttonRotation);
You can then calculate an easier direction number from this angle
int dir = (int)(Math.round(2.0d*pressed/Math.PI) % 4);
This gives right 0, up 1, left 2 and down 3. We need to correct the case where the angle is negative, as the modulo result will also be negative.
if (dir < 0) {
dir += 4;
}
Now supposing that these numbers are bad and you don't want to use them, you can just switch on the result to return whatever you like for each direction. Putting that all together:
/**
* #param dY
* The y difference between the touch position and the button
* #param dX
* The x difference between the touch position and the button
* #param buttonRotationDegrees
* The anticlockwise button rotation offset in degrees
* #return
* The direction number
* 1 = left, 2 = right, 3 = up, 4 = down, 0 = error
*/
public static int getAngle(int dY, int dX, double buttonRotationDegrees)
{
double pressed = Math.atan2(dY, dX);
pressed -= Math.toRadians(buttonRotationDegrees);
// right = 0, up = 1, left = 2, down = 3
int dir = (int)(Math.round(2.0d*pressed/Math.PI) % 4);
// Correct negative angles
if (dir < 0) {
dir += 4;
}
switch (dir) {
case 0:
return 2; // right
case 1:
return 3; // up
case 2:
return 1; // left;
case 3:
return 4; // down
}
return 0; // Something bad happened
}

Android Animation Question

I am trying to write the logic for an animation sequence and can't seem to get the thing right. What I want to happen is: if the user clicks on the screen, the method takes in the touchEvent coordinates, and then changes the movement variables of a sprite so that the sprite travels to where the user touched the screen. I have my "launch" event setup like this.
public void launch(float eventX, float eventY) {
//get the touch event coords and then move the banana to them.
fire = true;
//the x and y variables for the sprite
getX();
getY();
//the target x and y variables
targetX = eventX;
targetY = eventY;
//the total distance the two variable have to "travel"
distanceX = x - targetX;
distanceY = y - targetY;
//variables to update the movement
moveX = distanceX;
moveY = distanceY;
}
Then, I thought I was supposed to put the movement variables in the update method like this:
public void update(long gameTime) {
if(gameTime > frameTicker + framePeriod) {
frameTicker = gameTime;
currentFrame++;
if(currentFrame >= frameNbr){
currentFrame = 0;
}
}
this.sourceRect.left = currentFrame * spriteWidth;
this.sourceRect.right = this.sourceRect.left + spriteWidth;
if(fire == true){
x = (int) moveX;
y = (int) moveY;
}
If the user clicks as it is, the animation shows up like it's supposed to, but then instantaneously goes to the top left corner of the screen or what I have come to understand is (0,0) on a coordinate system. I can't figure out how to slow it down so that it moves at a reasonable space and goes where it is supposed to.
You could put the whole animation in your launch() function if you want.
For instance, at the end of the function something like:
float incrementX = distanceX / 100;
float incrementY = distanceY / 100;
float spriteX = getX();
float spriteY = getY();
bool xDone = false;
bool yDone = false;
while(!(xDone && yDone)) {
if (distanceX <= spriteX) {
spriteX += incrementX; // update the sprite's x coordinate as well
}
if (distanceY <= spriteY) {
spriteY += incrementY; // update the sprite's y coordinate as well
}
try{ Thread.sleep(10) } catch(Exception e) {}
}
That code relies on the sprite starting at a lower x and y than the event; if that's not the case it needs to be modified.

Categories

Resources