Get new position of coordinate after rotation with Matrix
I'm wondering how to use a Matrix to get the new position of a coordinate within a rectangle after rotation. What I would like to do is:
- Define a rectangle
- Define a coordinate within that rectangle
- Rotate the rectangle
- Get the new position of the coordinate after the rotation
The开发者_StackOverflow parts I can't figure out are 2 & 4. Any ideas?
I have created a simple Demo for this. It has a little extra, so you can also see how to use this in Drawing.
main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<SeekBar
android:id="@+id/seekBar1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
And the Activity:
package nl.entreco.android.testrotation;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
public class RotationActivity extends Activity implements OnSeekBarChangeListener {
private MyDrawing myDrawing;
private SeekBar mSeekbar;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Rect rect = new Rect(150,150,440,630);
int x = (int) (rect.left + Math.random() * rect.width());
int y = (int) (rect.top + Math.random() * rect.height());
Point coordinate = new Point(x, y);
// To draw the rect we create a CustomView
myDrawing = new MyDrawing(this, rect, coordinate);
RelativeLayout rl = (RelativeLayout)findViewById(R.id.container);
rl.addView(myDrawing);
mSeekbar = (SeekBar)findViewById(R.id.seekBar1);
mSeekbar.setMax(360);
mSeekbar.setOnSeekBarChangeListener(this);
}
private class MyDrawing extends View
{
private Rect myRect;
private Point myPoint;
private Paint rectPaint;
private Paint pointPaint;
private Matrix transform;
public MyDrawing(Context context, Rect rect, Point point)
{
super(context);
// Store the Rect and Point
myRect = rect;
myPoint = point;
// Create Paint so we can see something :)
rectPaint = new Paint();
rectPaint.setColor(Color.GREEN);
pointPaint = new Paint();
pointPaint.setColor(Color.YELLOW);
// Create a matrix to do rotation
transform = new Matrix();
}
/**
* Add the Rotation to our Transform matrix.
*
* A new point, with the rotated coordinates will be returned
* @param degrees
* @return
*/
public Point rotate(float degrees)
{
// This is to rotate about the Rectangles center
transform.setRotate(degrees, myRect.exactCenterX(), myRect.exactCenterY());
// Create new float[] to hold the rotated coordinates
float[] pts = new float[2];
// Initialize the array with our Coordinate
pts[0] = myPoint.x;
pts[1] = myPoint.y;
// Use the Matrix to map the points
transform.mapPoints(pts);
// NOTE: pts will be changed by transform.mapPoints call
// after the call, pts will hold the new cooridnates
// Now, create a new Point from our new coordinates
Point newPoint = new Point((int)pts[0], (int)pts[1]);
// Return the new point
return newPoint;
}
@Override
public void onDraw(Canvas canvas)
{
if(myRect != null && myPoint != null)
{
// This is an easy way to apply the same transformation (e.g. rotation)
// To the complete canvas.
canvas.setMatrix(transform);
// With the Canvas being rotated, we can simply draw
// All our elements (Rect and Point)
canvas.drawRect(myRect, rectPaint);
canvas.drawCircle(myPoint.x, myPoint.y, 5, pointPaint);
}
}
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,boolean fromUser) {
Point newCoordinates = myDrawing.rotate(progress);
// Now -> our float[] pts contains the new x,y coordinates
Log.d("test", "Before Rotate myPoint("+newCoordinates.x+","+newCoordinates.y+")");
myDrawing.invalidate();
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {}
}
Use Matrix.mapPoints to transform 2D points by matrix.
A long time late I know, but this was something I too was still confused about. This whole area of API seems more focused on doing things for us than letting us get at what's actually going on, no doubt because it's doing really clever stuff behind the scenes.
Setting points and getting them back are quite separate.
There are various ways to set a particular point, Entreco's excellent answer shows one way.
To get back a point you have to get the values of a matrix linked to that point and then pick the correct parts out of it. This, also excellent, answer (Android Matrix, what does getValues() return?) explains very clearly what's going on with the matrix, and you can see from that that the x,y values you want are elements indexed by 2 and 5.
The following is (slightly pseudo-)code I use to get at them.
float [] theArray = { <nine float zeroes> }
Matrix m = new Matrix();
boolean success = myPathMeasure.getMatrix(m, theArray, Matrix.MTRANS_X+Matrix.MTRANS_Y);
m.getValues(theArray);
x = theArray[2];
y = theArray[5];
I'm not terribly happy about this, but there doesn't seem to be a more formal way to do it.
精彩评论