I scaled my canvas as following (in onDraw):
canvas.scale(mScaleFactor, mScaleFactor, mScaleFocusX, mScaleFocusY);
My onScale function is:
private float mScaleFactor = 1.f;
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
mScaleFocusX = detector.getFocusX();
mScaleFocusY = detector.getFocusY();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
invalidate();
return true;
}
Now I get more points (using onTouchEvent) to create new path which eventually I want to draw.
onTouchEvent gives me x and y but doesn't take into account that a scale occurred.
How can I know where is the new position of x and y after the scaling?
From your question it is not clear what you do with mScaleFactor, mScaleFocusX, mScaleFocusY when rendering you canvas.
But, basically you just need to reverse this process to calculate the original coordinates of the touch (or any other form of interaction).
So for example if you calculate the render coordinates like this:
float renderX = originalX * mScaleFactor + mScaleFocusX;
float renderY = originalY * mScaleFactor + mScaleFocusY;
so would need to use
float correctedTouchX = (touchX - mScaleFocusX) / mScaleFactor;
float correctedTouchY = (touchY - mScaleFocusY) / mScaleFactor;
to calculate the corrected (original) coordinates. If you use mScaleFactor, mScaleFocusX, mScaleFocusY in some other way, you need to reverse that.
UPDATE: It seems that Canvas.scale() first uses mScaleFocusX and mScaleFocusY to define a pivot point (i.e. offset) and then scale. So you probably need these formulas (maybe change a minus into a plus sign somewhere):
float correctedTouchX = touchX / mScaleFactor - mScaleFocusX;
float correctedTouchY = touchY / mScaleFactor - mScaleFocusY;
Related
I've very annoying problem. Hope you can help me.
This part of my application is simple. When user taps NEXT or PREVIOUS button, imageview switches to previous or next image available. It works great but only if user does not use multitouch to ZOOM image. Please check images below to see what my problem is. I'm using TouchImageView library to zoom images.
IMAGE 1: This is image in it's normal state.
IMAGE 1 - ZOOM: User decides to use multitouch because it wants to ZOOM the image.
IMAGE 2: User taps Next button, but on the screen appears next image but it is in its ZOOMED state (from previous image).
Instead it should be like this:
Does anyone have any idea how to "reset" image and even if current picture is zoomed, next picture should not be zoomed?
Assuming you're talking about this TouchImageView, you could add a method that wraps the logic inside
public boolean onScale(ScaleGestureDetector detector);
in a method called setScaleFactor(float scaleFactor, float focusX, floatY);
That way, onScale method would look like.-
#Override
public boolean onScale(ScaleGestureDetector detector) {
float mScaleFactor = detector.getScaleFactor();
setScaleFactor(mScaleFactor, detector.getFocusX(), detector.getFocusY());
return true;
}
And you could call
setScaleFactor(1.0f, 0.5f, 0.5f);
to 'reset' the scale.
EDIT
Assuming you're working with the TouchImageView implementation given on the link above, setScaleFactor would look like this.-
public void setScaleFactor(float mScaleFactor, float focusX, float focusY) {
float origScale = saveScale;
saveScale *= mScaleFactor;
if (saveScale > maxScale) {
saveScale = maxScale;
mScaleFactor = maxScale / origScale;
} else if (saveScale < minScale) {
saveScale = minScale;
mScaleFactor = minScale / origScale;
}
if (origWidth * saveScale <= viewWidth || origHeight * saveScale <= viewHeight) {
matrix.postScale(mScaleFactor, mScaleFactor, viewWidth / 2, viewHeight / 2);
} else {
matrix.postScale(mScaleFactor, mScaleFactor, focusX, focusY);
}
fixTrans();
}
does anyone have any tips/ideas on how to calculate Round line joins?
The device I'm working on only supports single width lines.
I am trying to implement basic stroking with only round line joins.
Some things I'm messing with are below.
It's not much, but I hope to get some ideas on how to handle the different cases when two lines join, based on any replies.
Thanks in advance for any help.
I have had some progress with the outer join:
a. Get clockwise order vertices (I get these from flattened glyphs)
b. Grab 3 vertices
c. Compute normal of line A (prevX, prevY) -> (currentX, currentY)
d. Compute normal of line B (currentX, currentY) -> (nextX, nextY)
I compute normals using a left turn on the current clockwise vertices
normal = (deltaY, -deltaX) // Thanks Andreas
Vec2[] computeNormals(float prevX, float prevY, float x, float y, float nextX, float nextY) {
float dx1 = x - prevX;
float dy1 = y - prevY;
float dx2 = x - nextX;
float dy2 = y - nextY;
Vec2 normal1 = new Vec2(dy1, -dx1).normalize();
Vec2 normal2 = new Vec2(dy2, -dx2).normalize();
if (normal1.angleDeg() > normal2.angleDeg()) {
normal2.rot((float) Math.PI);
}
return (new Vec2[] { normal1, normal2 });
}
e. Determine outer join arc angle from atan2(deltaY, -deltaX)
void computeArc(VertexBuffer dest, float x, float y, float arcRadius, Vec2 normal1, Vec2 normal2) {
// Angle from Vecto2D is atan2(y, x)
float angleStart = normal1.angle();
float angleEnd = normal2.angle();
float angleInc = (float) Math.PI / 4f; // Temporary, need to find a way to determine numVertices for a Pen of a given width
while (angleStart > angleEnd) {
angleStart -= (float) (2f * Math.PI);
}
for (float a = angleStart; a <= angleEnd; a += angleInc) {
float vx = x + ((float) Math.cos(a) * arcRadius);
float vy = y + ((float) Math.sin(a) * arcRadius);
dest.addVertex(vx, vy);
}
}
If your device can draw filled circles, you could put a filled circle at the 2 end points, and one at every line joint.
I need to be able to move my player x and y pixels in the same direction as a point to a point. It's hard to explain, but this is what I am trying to do:
Angles 1 and 2 have to be the same. Point 1 stays the same at (100, 100), however point 2 constantly changes and so must the angles. I have tried this:
moveRectangle.setX(touchEvent.getX());
moveRectangle.setY(touchEvent.getY());
float theta = (float) Math.toDegrees(Math.atan2(touchEvent.getY() - 100,touchEvent.getY() - 100));
float velX = (float) (getSpeed() * Math.cos(theta));
float velY = (float) (getSpeed() * Math.sin(theta));
player.move(velX, velY);
The above code is constantly run when the user puts his finger on moveRectangle (Point 2) and moves it. But the above code does not work. The player just moves in one of two directions. player.move just adds velX and velY velocity. So how can I get the two angles and move the player in the right direction? Thanks.
Would it be easier to approach this problem using a cartesian approach (vectors) versus polar approach (angle and magnitude)? So, if the player is at point p0 and the "finger" is at point p1, then the direction the player should be moving v is given by the vector p1-p0. You can then scale the resulting vector v by the player's speed, and add the player's speed to his position. You can do this easily on a frame-by-frame basis.
Do you need just to know velocity on X and Y axis? You can do it without using trigonometry (just use Pythagorean theorem).
final float deltaX = touchEvent.getX() - player.x; // player.x is point1.x
final float deltaY = touchEvent.getY() - player.y; // player.y is point1.y
final float length = Maths.sqrt((deltaX)^2 + (deltaY)^2);
final float itterations = length / getSpeed();
final float velX = deltaX / itterations;
final float velY = deltaY / itterations;
player.move(velX, velY);
Or you need a code of player moving in cycle?
Remove Math.toDegrees().
From the Math.sin() / cos() Javadoc:
Parameters:
a - an angle, in radians.
Returns:
the sine of the argument.
I am trying to make some sort of 3D Editor with Java and OpenGL. And now I'm implementing the basic functions of an 3D Editor like rotating the camera around a specific Position and zooming. Next I want to do a 3D Picking to select Objects,Lines and Vertices in 3D-Space with the Mouse. I thought this is gonna to be easy because I can already select Objects when the Camera is focusing them.
Here is the example of the Selection of Objects with the Camera focus:
In the Class Camera there is this Method:
public boolean isVecInFocus(Vec3 vec) {
//returns the distance between camera and target
float c = new Vec3(posX,posY,posZ).getDistanceTo(vec);
// returns a Vector by drawing an imiginary line with the length of c and the position and rotation of the camera
Vec3 target = getFocusedPoint(c);
//checks if the calculated Vector is near to the target
if(target.x > vec.x - 0.05f && target.x < vec.x + 0.05f && target.y > vec.y - 0.05f && target.y < vec.y + 0.05f && target.z > vec.z - 0.05f && target.z < vec.z + 0.05f) {
return true;
} else {
return false;
}
}
Now, I want to do the same with the Mouse input:
//Mouse positions
float mX = Mouse.getX();
float mY = Mouse.getY();
//My test Vector
Vec3 vec = new Vec3(-5,5,-8);
//Camera Position
Vec3 camV = new Vec3(cam.getPosX(),cam.getPosY(),cam.getPosZ());
//Distance from Test Vector to Camera
float c = camV.getDistanceTo(vec);
//Calculating of the aspect between width and height (Because fov_x and fov_y are different because of the Screen Resolution, I think)
float aspect = (float) sb.getDisplayWidth() / (float) sb.getDisplayHeight();
//Normal fov refers to fov_y, so here is the fov_x
float fovx = cam.fov * aspect;
//Changing the Rotations to calculate the target Vector with the values of the Mouse position and rotations , not the Camera
float rotY = cam.getRotationY() + (fovx / (float) sb.getDisplayWidth()) * (mX) - (fovx / 2F);
float rotX = cam.getRotationX() + (cam.fov / (float) sb.getDisplayHeight()) * ((float) sb.getDisplayHeight() - mY) - (cam.fov / 2F);
//Calculating the target Vector with simple Math ...
double xDis = c * Math.sin(Math.toRadians(rotY)) * Math.cos(Math.toRadians(rotX));
double yDis = c * Math.sin(Math.toRadians(rotX));
double zDis = c * Math.cos(Math.toRadians(rotY)) * Math.cos(Math.toRadians(rotX));
float posX = (float) (camV.x + xDis);
float posY = (float) (camV.y - yDis);
float posZ = (float) (camV.z - zDis);
Vec3 target = new Vec3(posX,posY,posZ);
//Check if the target Vector and the Test Vector are the same.
If I use this Code, and point with my Mouse at the Test-Vector, the result is not right. The accuracy of the Point gets lower, the bigger the difference between Screen-middle and Mouse position is.
I think it has something to do with the OpenGL Perspective, but I'm not sure ...
So in my android game I'm making with andengine, I have it set up so as I touch and drag the player sprite, it continously rotates so that the character is always facing the direction it is traveling.
public class Player extends AnimatedSprite {
private float lastX = Game.centerX;
private float lastY = Game.centerY;
private static int angle = 0;
// ...
#Override
public boolean onAreaTouched(final TouchEvent sceneTouchEvent, final float touchAreaLocalX, final float touchAreaLocalY) {
Body body = OrbCatch.physicsWorld.getPhysicsConnectorManager().findBodyByShape(this);
if (sceneTouchEvent.getAction() == TouchEvent.ACTION_MOVE) {
float currentX = sceneTouchEvent.getX();
float currentY = sceneTouchEvent.getY();
angle = (int) (Math.atan2(currentY - lastY, currentX - lastX) * 180 / Math.PI);
lastX = currentX;
lastY = currentY;
}
body.setTransform(new Vector2(sceneTouchEvent.getX(),sceneTouchEvent.getY() )
.mul(1/PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT),angle);
return true;
}
}
The key line is this:
angle = (int) (Math.atan2(currentY - lastY, currentX - lastX) * 180 / Math.PI)
It takes the last known coordinates and the current coordinates, calculates the angle between them, and converts it from radians to degrees. Well, this was all working fine yesterday, but despite changing none of this code today it's behaving strangely. The sprite's orientation changes erratically, with no apparent pattern. If I move it in a straight path, it continuously alternates between 2 or 3 distinctly different angles(usually one of them is the correct one).
edit: solved, see below
The problem was that Body.setTransform angle parameter takes values in radians, not degrees. Andengine is so poorly documented...
math.todegree(math.atan(....))....
You should use toDegree().