Affine Transform recreation in android? - android

Need reproduce the affice transform in android device. i took the affine transform from the getMatrix() method
float[] matrixValues = new float[9];
matrix.getValues(matrixValues);
float a = matrixValues[0];
float b = matrixValues[3];
float tx = matrixValues[2];
float c = matrixValues[1];
float d = matrixValues[4];
float ty = matrixValues[5];
i recreate the angle using
float rAngle = Math.round(Math.atan2(v[Matrix.MSKEW_X], v[Matrix.MSCALE_X]) * (180 / Math.PI));
but the problem is that i need to apply
setPivotX(0);
setPivotY(0);
for getting the position of the image correct, why is this happening?

Related

Rendering Bitmap based on Views position in parent

I'm trying to make a simple image editor. At the beginning I've thought that it'll be a good idea to simply save view state as Bitmap but, as it turned out, there is a wide range of screen resolutions and that leads to huge quality (and memory usage) fluctuations.
Now I'm trying to make a module that renders views state translated to desired resolution.
In the code below I'm trying to recreate current state of the views in canvas:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.id.test_1_1);
bitmap = Bitmap.createScaledBitmap(bitmap, parentView.getMeasuredWidth(), parentView.getMeasuredHeight(), true);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
for (View rootView : addedViews) {
ImageView imageView = rootView.findViewById(R.id.sticker);
float[] viewPosition = new float[2];
transformToAncestor(viewPosition, parentView, imageView);
Bitmap originalBitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
Matrix adjustMatrix = new Matrix();
adjustMatrix.postTranslate(viewPosition[0], viewPosition[1]);
adjustMatrix.postScale(
rootView.getScaleX(),
rootView.getScaleY(),
rootView.getWidth() / 2,
rootView.getHeight() / 2);
adjustMatrix.postRotate(rootView.getRotation(),
rootView.getWidth() / 2,
rootView.getHeight() / 2);
canvas.drawBitmap(originalBitmap, adjustMatrix, paint);
}
transformToAncestor function is from here.
public static void transformToAncestor(float[] point, final View ancestor, final View descendant) {
final float scrollX = descendant.getScrollX();
final float scrollY = descendant.getScrollY();
final float left = descendant.getLeft();
final float top = descendant.getTop();
final float px = descendant.getPivotX();
final float py = descendant.getPivotY();
final float tx = descendant.getTranslationX();
final float ty = descendant.getTranslationY();
final float sx = descendant.getScaleX();
final float sy = descendant.getScaleY();
point[0] = left + px + (point[0] - px) * sx + tx - scrollX;
point[1] = top + py + (point[1] - py) * sy + ty - scrollY;
ViewParent parent = descendant.getParent();
if (descendant != ancestor && parent != ancestor && parent instanceof View) {
transformToAncestor(point, ancestor, (View) parent);
}
}
(author wrote a note that his function does not support rotation, but there's not much rotation in my example so I don't think that important for now).
My problem is:
First image is generated via saving the parent view state. Second one is generated by translating views position, rotation and scale onto canvas.
As you can see, on the canvas, not scaled stickers are positioned properly, but scaled are incorrectly positioned.
How to position those scaled views properly?
I've managed to fix the issue myself.
It turned out my solution was nearly OK but I did not took into consideration that my manipulation of a matrix does change the arrangement of the original points, so my
rootView.getWidth() / 2,
rootView.getHeight() / 2
is no longer applicable as a center of the view after calling Matrix.postScale or Matrix.postRotation.
I wanted to:
apply scale with pivot on top left corner,
apply rotation with pivot on the center of the view.
Given the assumptions, here's the working code:
// setup variables for sizing and transformation
float position[] = new float[2];
transformToAncestor(position, rootView, imageView);
float desiredRotation = imageView.getRotation();
float sizeDeltaX = imageView.getMeasuredWidth() / (float) imageBitmap.getWidth();
float sizeDeltaY = imageView.getMeasuredHeight() / (float) imageBitmap.getHeight();
float desiredScaleX = imageView.getScaleX() * sizeDeltaX * scaleX;
float desiredScaleY = imageView.getScaleY() * sizeDeltaY * scaleY;
float imageViewWidth = imageView.getMeasuredWidth() * imageView.getScaleX();
float imageViewHeight = imageView.getMeasuredHeight() * imageView.getScaleY();
float rootViewWidth = rootView.getMeasuredWidth();
float rootViewHeight = rootView.getMeasuredHeight();
float percentXPos = position[0] / rootViewWidth;
float percentYPos = position[1] / rootViewHeight;
float percentXCenterPos = (position[0] + imageViewWidth/2)
/ rootViewWidth;
float percentYCenterPos = (position[1] + imageViewHeight/2)
/ rootViewHeight;
float desiredPositionX = background.getWidth() * percentXPos;
float desiredPositionY = background.getHeight() * percentYPos;
float desiredCenterX = background.getWidth() * percentXCenterPos;
float desiredCenterY = background.getHeight() * percentYCenterPos;
// apply above variables to matrix
Matrix matrix = new Matrix();
float[] points = new float[2];
matrix.postTranslate(
desiredPositionX,
desiredPositionY);
matrix.mapPoints(points);
matrix.postScale(
desiredScaleX,
desiredScaleY,
points[0],
points[1]);
matrix.postRotate(
desiredRotation,
desiredCenterX,
desiredCenterY);
// apply matrix to bitmap, then draw it on canvas
canvas.drawBitmap(imageBitmap, matrix, paint);
As you can see, the mapPoints method was the answer for my question - it simply returns points after tranformation.

GearVRf position in sphere

I'm developing ad app withthe GearVR framework in which I must show a sphere and a series of interactive points on this sphere around the camera (a 360 photo with some points on the border). I could create the sphere witha a 360 photo and put the camera in but I can't figure out how to put the points onto the sphere because they are given to me related to the photo (photo is, like, 4608 x 2304 and my point is on the image 280 x 1115). How can I transalate the x,y coordinates to the sphere?
I tried many formulas but none seem to work. Here's my code, thanks in advance:
private int wSphere = 4608;
private int hSphere = 2304;
#Override
public void onInit(GVRContext gvrContext) {
GVRScene scene = gvrContext.getNextMainScene();
GVRSphereSceneObject sphereObject = null;
Future<GVRTexture> texture = gvrContext.loadFutureTexture(new GVRAndroidResource(gvrContext, R.raw.office));
sphereObject = new GVRSphereSceneObject(gvrContext, false, texture);
sphereObject.getTransform().setScale(50f, 50f, 50f);
Future<GVRTexture> futureTexture = gvrContext.loadFutureTexture(new GVRAndroidResource(gvrContext, R.raw.texture));
GVRMaterial material = new GVRMaterial(gvrContext);
material.setMainTexture(futureTexture);
float normX = (280f);
float normY = (1115f);
HashMap<String, Float> positions = xyToXYZ(normX, normY, 50);
GVRCubeSceneObject cubeObject = new GVRCubeSceneObject(gvrContext, true, material);
cubeObject.getTransform().setScale(5.5f, 5.5f, 5.5f);
cubeObject.getTransform().setPosition(positions.get("x"), positions.get("z"), positions.get("y"));
cubeObject.getRenderData().setMaterial(material);
scene.addSceneObject(sphereObject);
scene.addSceneObject(cubeObject);
}
public HashMap<String, Float> xyToXYZ(float x, float y, float r) {
HashMap<String, Float> map = new HashMap<String, Float>();
float theta = (float) (-2* Math.atan((Math.sqrt(Math.pow(x,2) + Math.pow(y,2)))/(2*r)) + 90);
float phi = (float) Math.atan((x/(-y)));
float sinTheta = (float) Math.sin(theta);
float cosTheta = (float) Math.cos(theta);
float sinPhi = (float) Math.sin(phi);
float cosPhi = (float) Math.cos(phi);
float nX = (float) (cosTheta * sinPhi);
float nY = (float) cosPhi * cosTheta;
float nZ = (float) sinTheta;
map.put("x", nX);
map.put("y", nY);
map.put("z", nZ);
return map;
}

How to update the azimuth with the rotation matrix from gyroscope?

Suppose I have my current orientation as (azimuth, pitch, roll). Now I wish to update my orientation with the gyroscope. According to the codes given by the Android development web, I can obtain the so-called deltaRotationMatrix as follows:
// Create a constant to convert nanoseconds to seconds.
private static final float NS2S = 1.0f / 1000000000.0f;
private final float[] deltaRotationVector = new float[4]();
private float timestamp;
public void onSensorChanged(SensorEvent event) {
// This timestep's delta rotation to be multiplied by the current rotation
// after computing it from the gyro sample data.
if (timestamp != 0) {
final float dT = (event.timestamp - timestamp) * NS2S;
// Axis of the rotation sample, not normalized yet.
float axisX = event.values[0];
float axisY = event.values[1];
float axisZ = event.values[2];
// Calculate the angular speed of the sample
float omegaMagnitude = sqrt(axisX*axisX + axisY*axisY + axisZ*axisZ);
// Normalize the rotation vector if it's big enough to get the axis
// (that is, EPSILON should represent your maximum allowable margin of error)
if (omegaMagnitude > EPSILON) {
axisX /= omegaMagnitude;
axisY /= omegaMagnitude;
axisZ /= omegaMagnitude;
}
// Integrate around this axis with the angular speed by the timestep
// in order to get a delta rotation from this sample over the timestep
// We will convert this axis-angle representation of the delta rotation
// into a quaternion before turning it into the rotation matrix.
float thetaOverTwo = omegaMagnitude * dT / 2.0f;
float sinThetaOverTwo = sin(thetaOverTwo);
float cosThetaOverTwo = cos(thetaOverTwo);
deltaRotationVector[0] = sinThetaOverTwo * axisX;
deltaRotationVector[1] = sinThetaOverTwo * axisY;
deltaRotationVector[2] = sinThetaOverTwo * axisZ;
deltaRotationVector[3] = cosThetaOverTwo;
}
timestamp = event.timestamp;
float[] deltaRotationMatrix = new float[9];
SensorManager.getRotationMatrixFromVector(deltaRotationMatrix, deltaRotationVector);
// User code should concatenate the delta rotation we computed with the current rotation
// in order to get the updated rotation.
// rotationCurrent = rotationCurrent * deltaRotationMatrix;
}
}
How should I proceed with this snippet so as to update my orientation?
You just need to multiply the deltaRotationMatrix by the rotationCurrentMatrix and then make a call to SensorManager.getOrientation(). You will need to implement a matrix multiplication method. You will also need an initial currentRotationMatrix, you can use the acceleration sensor and magnetic sensor with SensorManager.getRotationMatrix and SensorManager.getOrientation to get the initial currentRotationMatrix. Alternatively, you could use TYPE_ROTATION_VECTOR to get the initial currentRotationMatrix.
currentRotationMatrix = matrixMultiplication(
currentRotationMatrix,
deltaRotationMatrix);
SensorManager.getOrientation(currentRotationMatrix,
gyroscopeOrientation;
Unfortunately, what you will realize is that even the TYPE_GYROSCOPE sensor which is supposed to be calibrated for drift doesn't do a very good job and the sensor quickly drifts out of rotation with the device. Frustrating.
I have a GitHub repo with all of this implemented here
And a working project on the Play Store here

opengl 2.0 es view distance

I've just stated learning Opengl ES 2.0 in android and I run into a problem that i don't know how to solve.
I want to create a large plane field, I created it, put on texture but here comes my problem
It doesn't draw all of it it only displays about 10 unit on the Z axe.. X i fine.
So i want to create a big square but it displays a rectangle. It is like someone took a scissors and cut it off a certaint Z coordinate.
I don't even know what part of my code should i put it here, shader ? plane coordinates ? camera settings ?
Thank you for your patient.
It sounds like your plane is getting clipped by the frustum or viewing volume. That is typically set by either glOrtho() or glPerspective(). Try increasing the distance between the near and far plane parameters to these functions.
If you are relying on a default frustum provided by Android, you may have to construct your own frustum, which would look something like this for glOrtho():
typedef struct
{
float f0;
float f1;
float f2;
float f3;
float f4;
float f5;
float f6;
float f7;
float f8;
float f9;
float f10;
float f11;
float f12;
float f13;
float f14;
float f15;
} Mat4;
void Ortho(Mat4 * pMat4, float left, float top, float right, float bottom, float nearPlane, float farPlane)
{
float rcplmr = 1.0f / (left - right);
float rcpbmt = 1.0f / (bottom - top);
float rcpnmf = 1.0f / (nearPlane - farPlane);
pMat4->f0 = -2.0f * rcplmr;
pMat4->f1 = 0.0f;
pMat4->f2 = 0.0f;
pMat4->f3 = 0.0f;
pMat4->f4 = 0.0f;
pMat4->f5 = -2.0f * rcpbmt;
pMat4->f6 = 0.0f;
pMat4->f7 = 0.0f;
pMat4->f8 = 0.0f;
pMat4->f9 = 0.0f;
pMat4->f10 = -2.0f * rcpnmf;
pMat4->f11 = 0.0f;
pMat4->f12 = (right + left) * rcplmr;
pMat4->f13 = (top + bottom) * rcpbmt;
pMat4->f14 = (nearPlane + farPlane) * rcpnmf;
pMat4->f15 = 1.0f;
}

Find new control point when endpoint change in cubic bezier curve

I'm implementing cubic bezier curve logic in my one of Android Application.
I've implemented cubic bezier curve code on canvas in onDraw of custom view.
// Path to draw cubic bezier curve
Path cubePath = new Path();
// Move to startPoint(200,200) (P0)
cubePath.moveTo(200,200);
// Cubic to with ControlPoint1(200,100) (C1), ControlPoint2(300,100) (C2) , EndPoint(300,200) (P1)
cubePath.cubicTo(200,100,300,100,300,200);
// Draw on Canvas
canvas.drawPath(cubePath, paint);
I visualize above code in following image.
[Updated]
Logic for selecting first control points, I've taken ,
baseX = 200 , baseY = 200 and curve_size = X of Endpoint - X of Start Point
Start Point : x = baseX and y = baseY
Control Point 1 : x = baseX and y = baseY - curve_size
Control Point 2 : x = baseX + curve_size and y = baseY - curve_size
End Point : x = baseX + curve_size and y = baseY
I want to allow user to change EndPoint of above curve, and based on the new End points, I invalidate the canvas.
But problem is that, Curve maintain by two control points, which needs to be recalculate upon the change in EndPoint.
Like, I just want to find new Control Points when EndPoint change from (300,200) to (250,250)
Like in following image :
Please help me to calculate two new Control Points based on new End Point that curve shape will maintain same as previous end point.
I refer following reference links during searching:
http://pomax.github.io/bezierinfo/
http://jsfiddle.net/hitesh24by365/jHbVE/3/
http://en.wikipedia.org/wiki/B%C3%A9zier_curve
http://cubic-bezier.com/
Any reference link also appreciated in answer of this question.
changing the endpoint means two things, a rotation along P1 and a scaling factor.
The scaling factor (lets call it s) is len(p1 - p0) / len(p2 - p0)
For the rotation factor (lets call it r) i defer you to Calculating the angle between three points in android , which also gives a platform specific implementation, but you can check correctness by scaling/rotationg p1 in relation to p0, and you should get p2 as a result.
next, apply scaling and rotation with respect to p0 to c1 and c2. for convenience i will call the new c1 'd1' and the new d2.
d1 = rot(c1 - p0, factor) * s + p0
d2 = rot(c2 - p0, factor) * s + p0
to define some pseudocode for rot() (rotation http://en.wikipedia.org/wiki/Rotation_%28mathematics%29)
rot(point p, double angle){
point q;
q.x = p.x * cos(angle) - p.y * sin(angle);
q.y = p.x * sin(angle) + p.y * cos(angle);
}
Your bezier curve is now scaled and rotated in relation to p0, with p1 changed to p2,
Firstly I would ask you to look into following articles :
Bezier Curves
Why B-Spline Curve
B-Spline Curve Summary
What you are trying to implement is a piecewise composite Bézier curve. From the Summary page for n control points (include start/end) you get (n - 1)/3 piecewise Bézier curves.
The control points shape the curve literally. If you don't give proper control points with new point, you will not be able to create smoothly connected bezier curve. Generating them will not work, as it is too complex and there is no universally accepted way.
If you don't have/want to give extra control points, you should use Catmull-Rom spline, which passes through all control points and will be C1 continous (derivative is continuous at any point on curve).
Links for Catmull Rom Spline in java/android :
http://hawkesy.blogspot.in/2010/05/catmull-rom-spline-curve-implementation.html
https://github.com/Dongseob-Park/catmull-rom-spline-curve-android
catmull-rom splines for Android (similar to your question)
Bottom line is if you don't have the control points don't use cubic bezier curve. Generating them is a problem not the solution.
It seems that you are here rotating and scaling a square where you know the bottom two points and need to calculate the other two. The two known points form two triangles with the other two, so we just need to find the third point in a triangle. Supose the end point is x1, y1:
PointF c1 = calculateTriangle(x0, y0, x1, y1, true); //find left third point
PointF c2 = calculateTriangle(x0, y0, x1, y1, false); //find right third point
cubePath.reset();
cubePath.moveTo(x0, y0);
cubePath.cubicTo(c1.x, c1.y, c2.x, c2.y, x1, y1);
private PointF calculateTriangle(float x1, float y1, float x2, float y2, boolean left) {
PointF result = new PointF(0,0);
float dy = y2 - y1;
float dx = x2 - x1;
float dangle = (float) (Math.atan2(dy, dx) - Math.PI /2f);
float sideDist = (float) Math.sqrt(dx * dx + dy * dy); //square
if (left){
result.x = (int) (Math.cos(dangle) * sideDist + x1);
result.y = (int) (Math.sin(dangle) * sideDist + y1);
}else{
result.x = (int) (Math.cos(dangle) * sideDist + x2);
result.y = (int) (Math.sin(dangle) * sideDist + y2);
}
return result;
}
...
There is other way to do this where it does not matter how many points you have in between the first and the last point in the path or event its shape.
//Find scale
Float oldDist = (float) Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
Float newDist = (float) Math.sqrt((x2 - x0) * (x2 - x0) + (y2 - y0) * (y2 - y0));
Float scale = newDist/oldDist;
//find angle
Float oldAngle = (float) (Math.atan2(y1 - y0, x1 - x0) - Math.PI /2f);
Float newAngle = (float) (Math.atan2(y2 - y0, x2 - x0) - Math.PI /2f);
Float angle = newAngle - oldAngle;
//set matrix
Matrix matrix = new Matrix();
matrix.postScale(scale, scale, x0, y0);
matrix.postRotate(angle, x0, y0);
//transform the path
cubePath.transform(matrix);
A small variant on the suggestion by Lumis
// Find scale
Float oldDist = (float) Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
Float newDist = (float) Math.sqrt((x2 - x0) * (x2 - x0) + (y2 - y0) * (y2 - y0));
Float scale = newDist/oldDist;
// Find angle
Float oldAngle = (float) (Math.atan2(y1 - y0, x1 - x0));
Float newAngle = (float) (Math.atan2(y2 - y0, x2 - x0));
Float angle = newAngle - oldAngle;
Matrix matrix = new Matrix();
matrix.postScale(scale, scale);
matrix.postRotate(angle);
float[] p = { c1.x, c1.y, c2.x, c2.y };
matrix.mapVectors(p);
PointF newC1 = new PointF(p[0], p[1]);
PointF newC2 = new PointF(p[2], p[3]);

Categories

Resources