Rotating the Canvas impacts TouchEvents - android

I have a map application using an in-house map engine on Android. I'm working on a rotating Map view that rotates the map based on the phone's orientation using the Sensor Service. All works fine with the exception of dragging the map when the phone is pointing other than North. For example, if the phone is facing West, dragging the Map up still moves the Map to the South versus East as would be expected. I'm assuming translating the canvas is one possible solution but I'm honestly not sure the correct way to do this.
Here is the code I'm using to rotate the Canvas:
public void dispatchDraw(Canvas canvas)
{
canvas.save(Canvas.MATRIX_SAVE_FLAG);
// mHeading is the orientation from the Sensor
canvas.rotate(-mHeading, origin[X],origin[Y]);
mCanvas.delegate = canvas;
super.dispatchDraw(mCanvas);
canvas.restore();
}
What is the best approach to make dragging the map consistent regardless of the phones orientation? The sensormanager has a "remapcoordinates()" method but it's not clear that this will resolve my problem.

You can trivially get the delta x and delta y between two consecutive move events. To correct these values for your canvas rotation you can use some simple trignometry:
void correctPointForRotate(PointF delta, float rotation) {
// Get the angle of movement (0=up, 90=right, 180=down, 270=left)
double a = Math.atan2(-delta.x,delta.y);
a = Math.toDegrees(a); // a now ranges -180 to +180
a += 180;
// Adjust angle by amount the map is rotated around the center point
a += rotation;
a = Math.toRadians(a);
// Calculate new corrected panning deltas
double hyp = Math.sqrt(x*x + y*y);
delta.x = (float)(hyp * Math.sin(a));
delta.y = -(float)(hyp * Math.cos(a));
}

Related

I want to draw a route for directions which will have multiple turns

"I need to draw a transitioning line (like moving from point A to point B), a route line, i.e. line shouldn't appear suddenly, but it should be drawing from point to point like we see a loading line from left to right. I want to draw a route for directions which will have multiple turns."
I am drawing lines using onDraw, but they are appearing all of a sudden without animation, I am also moving an object along the line for an effect.
#Override
protected void onDraw(Canvas canvas) {
Log.d("tag1", "42222");
for (AnimationThing thing : animationThingsList){
Log.d("tag1", "47777");
//!!! Only the path of the last thing will be drawn on screen
canvas.drawPath(thing.animPath, thing.paint);
if (thing.distance < thing.pathLength) {
thing.pathMeasure.getPosTan(thing.distance, thing.pos, thing.tan);
thing.matrix.reset();
float degrees = (float) (Math.atan2(thing.tan[1], thing.tan[0]) * 180.0 / Math.PI);
thing.matrix.postRotate(degrees, thing.bm_offsetX, thing.bm_offsetY);
thing.matrix.postTranslate(thing.pos[0] - thing.bm_offsetX, thing.pos[1] - thing.bm_offsetY);
canvas.drawBitmap(thing.bm, thing.matrix, null);
thing.distance += thing.step;
} else {
thing.distance = 0;
}
break;
}
invalidate();
}
I need a line with a feeling being draw while the user views it.
If you add Google map you can take advantage of Google roads api and let them worry about drawing the route on the road, rather than you having to do the heavy load of calculating the optimized route on the map.

ARCore Pose and Aruco estimatePoseSingleMarkers

I have a server function that detects and estimates a pose of aruco's marker from an image.
Using the function estimatePoseSingleMarkers I found the rotation and translation vector.
I need to use this value in an Android app with ARCore to create a Pose.
The documentation says that Pose needs two float array (rotation and translation): https://developers.google.com/ar/reference/java/arcore/reference/com/google/ar/core/Pose.
float[] newT = new float[] { t[0], t[1], t[2] };
Quaternion q = Quaternion.axisAngle(new Vector3(r[0], r[1], r[2]), 90);
float[] newR = new float[]{ q.x, q.y, q.z, q.w };
Pose pose = new Pose(newT, newR);
The position of the 3D object placed in this pose is totally random.
What am I doing wrong?
This is a snapshot from server image after estimate and draw axis. The image I receive is rotated of 90°, not sure if it relates to anything.
cv::aruco::estimatePoseSingleMarkers (link) returns rotation vector in Rodrigues format. Following the doc
w = norm( r ) // angle of rotation in radians
r = r/w // unit axis of rotation
thus
float w = sqrt( r[0]*r[0] + r[1]*r[1] + r[2]*r[2] );
// handle w==0.0 separately
// Get a new Quaternion using an axis/angle (degrees) to define the rotation
Quaternion q = Quaternion.axisAngle(new Vector3(r[0]/w, r[1]/w, r[2]/w), w * 180.0/3.14159 );
should work except for the right angle rotation mentioned. That is, if the lens parameters are fed to estimatePoseSingleMarkers correctly or up to certain accuracy.

Change MapView orientation

I want to change the OSMdroid MapView orientation to face the direction which the user is going (calculated with Location.bearingTo between the previous and current user location on each onLocationChanged, and converted into normal degrees instead of the -180/180° East of True North degrees).
This direction is correct, I'm rotating an arrow image towards this direction and it points towards the right direction without fail.
However, when I want to orientate the MapView to these userDirection using the setMapOrientation method (documented here), this isn't working as I want it to be. When I orientate the map towards the user's direction, the arrow image should always be pointing north, right? Because this is want I want to achieve: to make it seem like the arrow is always pointing forward (like a GPS tracker: your location on GPS is always represented by an icon going forward, my arrow is pointing to all kinds of directions because the map orientation is wrong).
I'm guessing the osmdroid.MapView orientation is expecting another sort of degree value, but I've tried converting back to East of True North degrees, didn't work. Or my logic is completely wrong and it is working correctly.
How do set the orientation for the MapView so that it is always facing the user's current direction, so that the arrow is always pointing forward (and not going backwards, right or left, ... )?
I think what you are referring to as is "True North" orientation of Map using the compass True North. For this you need the device Compass or Sensor Listener to get the direction, after getting the heading you need to set it for the MapView. Here is the Snippet which is very helpful.
private void compassHeadingUp() {
if(enableCompassHeadUp){
mSensorManager.registerListener(mySensorEventListener,
SensorManager.SENSOR_ORIENTATION,
SensorManager.SENSOR_DELAY_FASTEST);
} else {
mSensorManager.unregisterListener(mySensorEventListener);
mDirection = 0;
}
}
public SensorListener mySensorEventListener = new SensorListener(){
#Override
public void onAccuracyChanged(int arg0, int arg1) {
}
#Override
public void onSensorChanged(int sensor, float[] values) {
synchronized (this) {
float mHeading = values[0];
if(Math.abs(mDirection-mHeading) > Constance.ROTATION_SENSITIVITY){
mMapView.setMapOrientation(-mHeading);
mDirection = mHeading;
}
Matrix matrix = new Matrix();
mCompassImageView.setScaleType(ScaleType.MATRIX);
matrix.postRotate((float) -mHeading, mCompassImageView.getDrawable().getBounds().width()/2, mCompassImageView.getDrawable().getBounds().height()/2);
//Set your Arrow image view to the matrix
mCompassImageView.setImageMatrix(matrix);
}
}
};
I have solved this issue by inverting degrees like this:
float bearing = location.getBearing();
float t = (360 - bearing);
if (t < 0) {
t += 360;
}
if (t > 360) {
t -= 360;
}
//help smooth everything out
t = (int) t;
t = t / 5;
t = (int) t;
t = t * 5;
mapOSM.setMapOrientation(t);

rotate 3D Object (Spatial) with compass

I've generated 3D overlay using jMonkey in my Android app. Everything works fine - my ninja model is walking in loop. Awsome !
Now I want to rotate camera according to direction of the phone. I thought compass is the best way BUT unfortunatly I have a lot of problems. So here I go
I've created method that is invoked in activity
public void rotate(float x, float y, float z) {
Log.d(TAG, "simpleUpdate: Nowa rotacja: " + y);
newX = x;
newY = y;
newZ = z;
newPosition = true;
}
in 'simpleUpdate' method I've managed it this way
if(newPosition && ninja != null) {
Log.d(TAG, "simpleUpdate: rotacja: " + newY);
ninja.rotate((float)Math.toRadians(newX), (float)Math.toRadians(newY), (float)Math.toRadians(newZ));
newPosition = false;
}
in my activity I'm checking if phone moved
if(lastAzimuth != (int)azimuthInDegress) {
lastAzimuth = (int)azimuthInDegress;
I cast to int so the distortion won't be so big problem
if ((com.fixus.towerdefense.model.SuperimposeJME) app != null) {
((com.fixus.towerdefense.model.SuperimposeJME) app).rotate(0f, azimuthInDegress, 0f);
}
At the moment I want to rotate it only in Y axis
Now the main problem is that the rotations is more like jump that rotation. When I move my phone a bit and I have 6 degrees diffrence (i see this in my log) the model is rotated like for 90 degrees and he turns back. This has nothing to do with rotation or change taken from my compas.
Any ideas ?
UPDATE
I think I got it. Method rotate, rotates from current state with value I've set. So it looks more like old Y rotate + new value. So I'm setting the diffrence between current value and old value and it now look almoust fine. Is it the good way ?

Bitmap Matrix postRotate changes X and Y axis direction

I'm trying to rotate my Bitmap using a readymade solution I found somewhere. The code is below:
public void onDraw(Canvas canvas) {
float x = ship.Position.left;
float y = ship.Position.top;
canvas.drawBitmap(ship.ship, x,y,null);
invalidate();
}
However, when I do it, the X and Y axii change their direction - if I increase the Y the image goes towards the top of the screen, not towards the bottom. Same happens to X if I rotate by 90 degrees.
I need to rotate it but without changing the Y and X axii directions.
Even rotated, I still want the Bitmap to go towards the bottom if I increase Y and to the right if I increase the X.
public void update()
{
if(!moving)
{
fall();
}
else //moving
{
move();
faceDirection();
}
Position.top += Speed;
}
private void move() {
if(Speed < MAXSPEED)
Speed -= 0.5f;
}
private void fall() {
if(Speed > MAXSPEED*-1)
Speed += 0.2f;
}
private void faceDirection() {
double OldDiretion = Direction;
Direction = DirectionHelper.FaceObject(Position, ClickedDiretion);
if (Direction != OldDiretion)
{
Matrix matrix = new Matrix();
matrix.postRotate((float)Direction);
ship = Bitmap.createBitmap(ship, 0, 0, ship.getWidth(),ship.getHeight(), matrix, false);
}
I tried the code above, but it's still changing the Y direction, It's going to bottom of the BitMap, not bottom of the screen.
Here is the project: https://docs.google.com/file/d/0B8V9oTk0eiOKOUZJMWtsSmUtV3M/edit?usp=sharing
You should first rotate, than translate:
matrix.postTranslate(x, y);
matrix.postRotate(degree);
alternative would be to try to use preRotate() instead of postRotate().
I also strongly recommend to translate/rotate the original while drawing. So your createBitmap() call shouldn't modify the orientation. At least not when you change it dynamically on user interaction. Otherwise you would create a lot of bitmaps to represent rotations over and over again which would impact the performance.
The problem is that you don't actually rotate the bitmap - you just draw it rotated. So, the next time you redraw it, you first push it towards the bottom or right by incrementing x/y and then rotate it.
You have to actually rotate the bitmap itself. You could use the following code:
ship.ship = Bitmap.createBitmap(ship.ship, 0, 0, ship.ship.getWidth(), ship.ship.getHeight(), matrix, false);
Here you create a new rotated bitmap and set your reference to point to it.
Note! You must do this only once! So you can't do it in the onDraw method, since then it will get rotated every time it's redrawn. You have to do it somewhere else and then draw it as usual in onDraw (without the matrix rotations).

Categories

Resources