How to get a 3D accordion effect on Android - android

I am planning to port the great 3D accordion effect found on iOS (https://github.com/xyfeng/XYOrigami) on Android.
so far I have something pretty close, here is a screenshot with "pinch-to-fold" to fold the view:
http://imageshack.us/photo/my-images/822/3daccordion.png
as you can see, it remains a space between 2 opposite "panels".
The process is simple:
take the canvas of the main Layout
create a Custom surface view which will replace all the view of the layout
set as background image the taken canvas
cut this canvas into X panels (here 16)
redraw the surfaceview with all the "panels" with a calculated matrix
recalculate the matrix while pinching to give the "folding effect"
here is the transformation matrix :
private Matrix getFoldingMatrix(int angle, int startFrom,int position) {
int move = (int) (
(mWidth - (Math.cos(angle * Math.PI / 180) * mWidth)) * position
) +1*position; //overlap of 1px the panels and not get space between panels
final Camera camera = new Camera();
final Matrix matrix = new Matrix();
camera.save();
camera.rotateY(angle);
camera.getMatrix(matrix);
if(position%2==1){
move += (int) (
(mWidth - (Math.cos(angle * Math.PI / 180) * mWidth))
) ;
matrix.preTranslate(-mWidth, -mHeight/2);
matrix.postTranslate(startFrom+mWidth-move, mHeight/2);
} else {
matrix.preTranslate(0f, -mHeight/2);
matrix.postTranslate(startFrom-move, mHeight/2);
}
camera.restore();
return matrix;
}
what am I getting wrong ? why is there still these spaces between opposite "panels"?

Related

Card flip Animation not looking good on Android 7 Nougat

thank you for reading my question.
The problem I have only occurs in android 7.0 (Nougat) and what it's happening is that the card (a fragment with Image views) when it rotates in the animation, it suddenly crops part of the image, Look:
Android 7 Nougat Screenshoot
Card in rotation
Android 4.4 Kitkat Screenshoot
Card in rotation
I'm using Animation (not objectAnimator yet), Here's my code:
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
// Angle around the y-axis of the rotation at the given time
// calculated both in radians and degrees.
final double radians = Math.PI * interpolatedTime;
float degrees = (float) (180.0 * radians / Math.PI);
Log.d(LOG_TAG," "+interpolatedTime);
// Once we reach the midpoint in the animation, we need to hide the
// source view and show the destination view. We also need to change
// the angle by 180 degrees so that the destination does not come in
// flipped around
if (interpolatedTime >= 0.5f) {
degrees -= 180.f;
fromView.setVisibility(View.GONE);
toView.setVisibility(View.VISIBLE);
}
if (forward)
degrees = -degrees; //determines direction of rotation when flip begins
//computing
final Matrix matrix = t.getMatrix();
camera.save();
camera.translate(0, 0, Math.abs(degrees) * 3f);
camera.rotateY(degrees);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
}
Thank you so much for sharing your ideas. :)
Read the https://developer.android.com/reference/android/view/View.html#setCameraDistance(float)
You have to set the camera distance.
I know that this question is old, but I hope that will help somebody.

Rotate a text in a bitmap using canvas

The bitmap only contain the text so the width and the height of the bitmap are determined by text's width and height and some math. now when i rotate the text using
canvas.rotate(tilt - 180, bitmap.getWidth() / 2, bitmap.getHeight() / 2);
//the seek bar has a range of 0 to 360 so in order to get the right tilt i need to use (tilt - 180) instead of (tilt)
this is what happen
well obviously center of the rotation is wrong. I'v tried to use canvas.translate and the result have got better but they are yet not correct:
float actualTilt = Math.abs(tilt - 180);
actualTilt = (actualTilt >= 90 && actualTilt != 180) ? 90 - actualTilt % 90 : actualTilt;
if (actualTilt == 180) actualTilt = 0;
// by above statements i make sure actualTilt is less than 90 and it's values are 0,1,...89,90,89,88,...0
float v = actualTilt / 90;
canvas.translate((-(textWidth / 2) * v), (textHeight / 2) * v);
//i don't understand the above statement either but this is the result
if texts get longer the result get even worst
I found here the following codeto rotate a bitmap in a canvas:
Matrix matrix = new Matrix();
matrix.setRotate(angle, imageCenterX, imageCenterY);
yourCanvas.drawBitmap(yourBitmap, matrix, null);
Have you tried this out?

Flip animation clips the previously scaled ImageView on API 19

I've played with the FlipImageView project and noticed one rather curious issue. When I'm running the sample contained within the project, everything works just fine. But, when I scale the FlipImageView manually (using View.setScaleX(float) and View.setScaleY(float) methods) this sample works fine just for devices with API version 16-18, and it clips the FlipImageView during the animation to the size of the unscaled image if sample is run on the device with the 19-th version of API. Here are screenshots:
The star is clipped, API 19
The star isn't clipped, API 18
I've set the visibility of all other widgets to GONE and changed the image's width to wrap_content to make an issue even more obvious. I see this behavior both on emulators and on real devices.
The code of the custom animation looks like so:
public class FlipAnimator extends Animation {
// fields declarations omitted
public void setToDrawable(Drawable to) {
toDrawable = to;
visibilitySwapped = false;
}
public FlipAnimator() {
setFillAfter(true);
}
#Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
camera = new Camera();
this.centerX = width / 2;
this.centerY = height / 2;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final double radians = Math.PI * interpolatedTime;
float degrees = (float) (180.0 * radians / Math.PI);
if(mIsRotationReversed){
degrees = -degrees;
}
if (interpolatedTime >= 0.5f) {
if(mIsRotationReversed){ degrees += 180.f; } else{ degrees -= 180.f; }
if (!visibilitySwapped) {
setImageDrawable(toDrawable);
visibilitySwapped = true;
}
}
final Matrix matrix = t.getMatrix();
camera.save();
camera.translate(0.0f, 0.0f, (float) (150.0 * Math.sin(radians)));
camera.rotateX(mIsRotationXEnabled ? degrees : 0);
camera.rotateY(mIsRotationYEnabled ? degrees : 0);
camera.rotateZ(mIsRotationZEnabled ? degrees : 0);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
}
}
I've searched through the diff between API version 18 and 19 and didn't find any changes in the Matrix or Camera classes which are used in the Animation.applyTransformation() method. I didn't find any changes in the Animation class itself neither.
What is the source of this problem?
Upd: the clipping occurs only during the animation - when it ends, the FlipImageView is drawn in its full size.

Get draw bounds/rect of a view

I'm developing an app where a lot of views can be rotated - it's something like a map of physical objects. I have to detect when 2 objects (all objects are rectangles/squares) are overlapping and if a user has performed a single/double/long tap on an object. For this reason I need to know the drawing bounds of a view.
Let's look at the example image bellow - the green rectangle is rotated 45 degrees. I need to get the coordinates of the 4 corners of the green rectangle. If I use view.getHitRect() it returns the bounding box (marked in red) of the view, which is of no use to me.
Do you know how could I get the coordinates of the edges of a view?
The only solution I could think of is to subclass a View, manually store the initial coordinates of the corners and calculate their new values on every modification to the view - translation, scale and rotation but I was wondering if there is a better method.
P.S. The app should be working on Android 2.3 but 4.0+ solutions are also welcomed.
Thanks to pskink I explored again the Matrix.mapPoints method and managed to get the proper coordinates of the corners of the rectangle.
If you are running on Android 3.0+ you can easily get the view's matrix by calling myView.getMatrix() and map the points of interest. I had to use 0,0 for the upper left corner and getWidth(),getHeight() for the bottom right corner and map these coordinates to the matrix. After that add view's X and Y values to get the real values of the corners.
Something like:
float points[] = new float[2];
points[0] = myView.getWidth();
points[1] = myView.getHeight();
myView.getViewMatrix().mapPoints(points);
Paint p = new Paint();
p.setColor(Color.RED);
//offset the point and draw it on the screen
canvas.drawCircle(center.getX() + points[0], center.getY() + points[1], 5f, p);
If you have to support lower versions of Android you can use NineOldAndroids. Then I've copied and modified one of its internal methods to get the view's matrix:
public Matrix getViewMatrix()
{
Matrix m = new Matrix();
Camera mCamera = new Camera();
final float w = this.getWidth();
final float h = this.getHeight();
final float pX = ViewHelper.getPivotX(this);
final float pY = ViewHelper.getPivotY(this);
final float rX = ViewHelper.getRotationX(this);;
final float rY = ViewHelper.getRotationY(this);
final float rZ = ViewHelper.getRotation(this);
if ((rX != 0) || (rY != 0) || (rZ != 0))
{
final Camera camera = mCamera;
camera.save();
camera.rotateX(rX);
camera.rotateY(rY);
camera.rotateZ(-rZ);
camera.getMatrix(m);
camera.restore();
m.preTranslate(-pX, -pY);
m.postTranslate(pX, pY);
}
final float sX = ViewHelper.getScaleX(this);
final float sY = ViewHelper.getScaleY(this);;
if ((sX != 1.0f) || (sY != 1.0f)) {
m.postScale(sX, sY);
final float sPX = -(pX / w) * ((sX * w) - w);
final float sPY = -(pY / h) * ((sY * h) - h);
m.postTranslate(sPX, sPY);
}
m.postTranslate(ViewHelper.getTranslationX(this), ViewHelper.getTranslationY(this));
return m;
}
I've put this method in an overloaded class of a view (in my case - extending TextView). From there on it's the same as in Android 3.0+ but instead of calling myView.getMatrix() you call myView.getViewMatrix().

android opengl check visibility of a point with camera zoom

I m woring on an android opengl 1.1 2d game with a top view on a vehicule and a camera zoom relative to the vehicule speed. When the speed increases the camera zoom out to offer the player a best road visibility.
I have litte trouble finding the exact way to detect if a sprite is visible or not regarding his position and the current camera zoom.
Important precision, all of my game's objects are on the same z coord. I use 3d just for camera effect. (that's why I do not need frustrum complicated calculations)
here is a sample of my GLSurfaceView.Renderer class
public static float fov_degrees = 45f;
public static float fov_radians = fov_degrees / 180 * (float) Math.PI;
public static float aspect; //1.15572 on my device
public static float camZ; //927 on my device
#Override
public void onSurfaceChanged(GL10 gl, int x, int y) {
aspect = (float) x / (float) y;
camZ = y / 2 / (float) Math.tan(fov_radians / 2);
Camera.MINIDECAL = y / 4; // minimum cam zoom out (192 on my device)
if (x == 0) { // Prevent A Divide By Zero By
x = 1; // Making Height Equal One
}
gl.glViewport(0, 0, x, y); // Reset The Current Viewport
gl.glMatrixMode(GL10.GL_PROJECTION); // Select The Projection Matrix
gl.glLoadIdentity(); // Reset The Projection Matrix
// Calculate The Aspect Ratio Of The Window
GLU.gluPerspective(gl, fov_degrees, aspect , camZ / 10, camZ * 10);
GLU.gluLookAt(gl, 0, 0, camZ, 0, 0, 0, 0, 1, 0); // move camera back
gl.glMatrixMode(GL10.GL_MODELVIEW); // Select The Modelview Matrix
gl.glLoadIdentity(); // Reset The Modelview Matrix
when I draw any camera relative object I use this translation method :
gl.glTranslatef(position.x - camera.centerPosition.x , position.y -camera.centerPosition.y , - camera.zDecal);
Eveything is displayed fine, the problem comes from my physic thread when he checks if an object is visible or not:
public static boolean isElementVisible(Element element) {
xDelta = (float) ((camera.zDecal + GameRenderer.camZ) * GameRenderer.aspect * Math.atan(GameRenderer.fov_radians));
yDelta = (float) ((camera.zDecal + GameRenderer.camZ)* Math.atan(GameRenderer.fov_radians));
//(xDelta and yDelta are in reallity updated only ones a frame or when camera zoom change)
Camera camera = ObjectRegistry.INSTANCE.camera;
float xMin = camera.centerPosition.x - xDelta/2;
float xMax = camera.centerPosition.x + xDelta/2;
float yMin = camera.centerPosition.y - yDelta/2;
float yMax = camera.centerPosition.y + yDelta/2;
//xMin and yMin are supposed to be the lower bounds x and y of the visible plan
// same for yMax and xMax
// then i just check that my sprite is visible on this rectangle.
Vector2 phD = element.getDimToTestIfVisibleOnScreen();
int sizeXd2 = (int) phD.x / 2;
int sizeYd2 = (int) phD.y / 2;
return (element.position.x + sizeXd2 > xMin)
&& (element.position.x - sizeXd2 < xMax)
&& (element.position.y - sizeYd2 < yMax)
&& (element.position.y + sizeYd2 > yMin);
}
Unfortunately the object were disapearing too soon and appearing to late so i manuelly added some zoom out on the camera for test purpose.
I did some manual test and found that by adding approx 260 to the camera z index while calculating xDelta and yDelta it, was good.
So the line is now :
xDelta = (float) ((camera.zDecal + GameRenderer.camZ + 260) * GameRenderer.aspect * Math.atan(GameRenderer.fov_radians));
yDelta = (float) ((camera.zDecal + GameRenderer.camZ + 260)* Math.atan(GameRenderer.fov_radians));
Because it's a hack and the magic number may not work on every device I would like to understand what i missed. I guess there is something in that "260" magic number that comes from the fov or ration width/height and that could be set as a formula parameter for pixel perfect detection.
Any guess ?
My guess is that you should be using Math.tan(GameRenderer.fov_radians) instead of Math.atan(GameRenderer.fov_radians).
Reasoning:
If you used a camera with 90 degree fov, then xDelta and yDelta should be infinitely large, right? Since the camera would have to view the entire infinite plane.
tan(pi/2) is infinite (and negative infinity). atan(pi/2) is merely 1.00388...
tan(pi/4) is 1, compared to atan(pi/4) of 0.66577...

Categories

Resources