Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android view 3d rotate transformation on big resolution screens

I'm implementing 3d card flip animation for android (api > 14) and have an issue with big screen tablets (> 2048 dpi). During problem investigation i've come to the following basic block:

Tried to just transform a view (simple ImageView) using matrix and rotateY of camera by some angle and it works ok for angle < 60 and angle > 120 (transformed and displayed) but image disappears (just not displayed) when angle is between 60 and 120. Here is the code I use:

private void applyTransform(float degree) 
{
     float [] values = {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f};
     float centerX = image1.getMeasuredWidth() / 2.0f;
     float centerY = image1.getMeasuredHeight() / 2.0f;

     Matrix m = new Matrix();
     m.setValues(values);

     Camera camera = new Camera();
     camera.save();

     camera.rotateY(degree);
     camera.getMatrix(m);

     camera.restore();

     m.preTranslate(-centerX, -centerY); // 1 draws fine without these 2 lines
     m.postTranslate(centerX, centerY);  // 2 

    image1.setImageMatrix(m);
}

And here is my layout XML

   <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout     xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:id="@+id/ImageView01"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:src="@drawable/naponer"
        android:clickable="true"
        android:scaleType="matrix">
    </ImageView>
</FrameLayout>  

So I have the following cases:

  • works fine for any angle, any center point if running on small screens 800X480, 1024x720, etc...
  • works ok for angle < 60 and > 120 when running on big screen devices 2048x1536, 2560x1600...
  • works ok for any angle on any device if rotation not centered (matrix pre and post translations commented out )
  • fails (image disappears) when running on big screen device, rotation centered and angle is between 60 and 120 degrees.

Please tell what I'm doing wrong and advise some workaround... thank you!!!

like image 297
Henrik Avatar asked Dec 30 '14 11:12

Henrik


1 Answers

This problem is caused by the camera distance used to calculate the transformation. While the Camera class itself doesn't say much about the subject, it is better explained in the documentation for the View.setCameraDistance() method (emphasis mine):

Sets the distance along the Z axis (orthogonal to the X/Y plane on which views are drawn) from the camera to this view. The camera's distance affects 3D transformations, for instance rotations around the X and Y axis. (...)

The distance of the camera from the view plane can have an affect on the perspective distortion of the view when it is rotated around the x or y axis. For example, a large distance will result in a large viewing angle, and there will not be much perspective distortion of the view as it rotates. A short distance may cause much more perspective distortion upon rotation, and can also result in some drawing artifacts if the rotated view ends up partially behind the camera (which is why the recommendation is to use a distance at least as far as the size of the view, if the view is to be rotated.)

To be honest, I hadn't seen this particular effect (not drawing at all) before, but I suspected it could be related to this question related to perspective distortion I'd encountered in the past. :)

Therefore, the solution is to use the Camera.setLocation() method to ensure this doesn't happen.

An important distinction with the View.setCameraDistance() method is that the units are not the same, since setLocation() doesn't use pixels. While setCameraDistance() adjusts for density, setLocation() does not. Therefore, if you wanted to calculate an appropriate z-distance based on the view's dimensions, remember to adjust for density. For example:

float cameraDistance = Math.max(image1.getMeasuredHeight(), image1.getMeasuredWidth()) * 5;
float densityDpi = getResources().getDisplayMetrics().densityDpi;
camera.setLocation(0, 0, -cameraDistance / densityDpi);
like image 185
matiash Avatar answered Oct 12 '22 07:10

matiash