Android Canvas. Moving and Rotating Bitmap along Circular Path based on Touch? - android

Is it Possible to move and rotate an Image along a Circular Path based on a touch event as follows:
I have looked at this question:
Moving an Image in circular motion based on touch events in android
But it only tells me how to move the image along a circle, not rotate it.

Update: Full example posted on GitHub at https://github.com/jselbie/xkcdclock
Every time you get a touch event, grab the touch point's x,y coordinates and compute the angle of the rotation relative to the center of bitmap. Use that value to determine how much to rotate the bitmap you want draw.
First, let's assume a logical coordinate system in which the center point of your element above is at (0,0) in x,y space.
Therefore, the angle (in degrees) between any touch point relative to the center can be computed as follows:
double ComputeAngle(float x, float y)
{
final double RADS_TO_DEGREES = 360 / (java.lang.Math.PI*2);
double result = java.lang.Math.atan2(y,x) * RADS_TO_DEGREES;
if (result < 0)
{
result = 360 + result;
}
return result;
}
Note - the normalization of negative angles to positive angles. So if the touch point is (20,20), this function above will return 45 degrees.
To make use of this method, your Activity will need the following member variables defined:
float _refX; // x coordinate of last touch event
float _refY; // y coordinate or last touch event
float _rotation; // what angle should the source image be rotated at
float _centerX; // the actual center coordinate of the canvas we are drawing on
float _centerY; // the actual center coordinate of the canvas we are drawing on
Now let's examine how to keep track of touch coordinates to we can always have an up to date "_rotation" variable.
So our "touch handler" for Android will look something like this:
boolean onTouch(View v, MotionEvent event)
{
int action = event.getAction();
int actionmasked = event.getActionMasked();
if (!_initialized)
{
// if we haven't computed _centerX and _centerY yet, just bail
return false;
}
if (actionmasked == MotionEvent.ACTION_DOWN)
{
_refX = event.getX();
_refY = event.getY();
return true;
}
else if (actionmasked == MotionEvent.ACTION_MOVE)
{
// normalize our touch event's X and Y coordinates to be relative to the center coordinate
float x = event.getX() - _centerX;
float y = _centerY - event.getY();
if ((x != 0) && (y != 0))
{
double angleB = ComputeAngle(x, y);
x = _refX - _centerX;
y = _centerY - _refY;
double angleA = ComputeAngle(x,y);
_rotation += (float)(angleA - angleB);
this.invalidate(); // tell the view to redraw itself
}
}
There's some fine details left out such as drawing the actual bitmap. You might also want to handle the ACTION_UP and ACTION_CANCEL events to normalize _rotation to always be between 0 and 360. But the main point is that the above code is a framework for computing the _rotation at which your Bitmap should be drawn on the View. Something like the following:
void DrawBitmapInCenter(Bitmap bmp, float scale, float rotation, Canvas canvas)
{
canvas.save();
canvas.translate(canvas.getWidth()/2, canvas.getHeight()/2);
canvas.scale(scale, scale);
canvas.rotate(rotation);
canvas.translate(-bmp.getWidth()/2, -bmp.getHeight()/2);
canvas.drawBitmap(bmp, 0, 0, _paint);
canvas.restore();
}

Related

How to move a Path on a Canvas in Android?

I am trying to make an Android paint application for finger painting and I am having trouble with moving the lines I draw.
What I tried to do was offset the path of the currently selected line by the difference between the initial finger press coordinates and the current coordinates in OnTouchEvent during ACTION_MOVE.
case MotionEvent.ACTION_MOVE:
selectline.getLine().offset(x - otherx, y - othery);
otherx and othery are set as the x and y coordinates during ACTION_MOVE and x and y are the current cursor coordinates. My lines are stored as a separate class containing the path, color, thickness and bounding box.
What I got was the shape flying off the screen in the direction of my finger without stopping at the slightest movement. I tried using a matrix to move the path, but the result was the same.
When I tried to insert a "do while" that would check whether the current coordinates would match the path's .computeBounds() rectangle center, but the program crashes as soon as I move my finger.
Any help would be appreciated, thanks.
Most likely that you did not use the right scale for the coordinates.
Source: Get Canvas coordinates after scaling up/down or dragging in android
float px = ev.getX() / mScaleFactor + rect.left;
float py = ev.getY() / mScaleFactor + rect.top;
// where mScaleFactor is the scale use in canvas and rect.left and rect.top is the coordinate of top and left boundary of canvas respectively
Its a bit late but it may solve others problem. I solved this issue like this,
get initial X,Y position on onLongPress
public void onLongPress(MotionEvent motionEvent) {
try {
shapeDrag = true;
SmEventX = getReletiveX(motionEvent);
SmEventY = getReletiveY(motionEvent);
} catch (Exception e) {
e.printStackTrace();
}
and then on onToucn(MotionEvent event)
case MotionEvent.ACTION_MOVE: {
actionMoveEvent(motionEvent);
try {
if (shapeDrag) {
StylePath sp = alStylePaths
.get(alStylePaths.size() - 1);
Path mpath = sp.getPath();
float tempX = getReletiveX(motionEvent) - SmEventX;
float tempY = getReletiveY(motionEvent) - SmEventY;
mpath.offset(tempX, tempY);
SmEventX = getReletiveX(motionEvent);
SmEventY = getReletiveY(motionEvent);
}
} catch (Exception e) {
e.printStackTrace();
}
break;
}
}
I faced the same trouble and in my case it was a very naive mistake. Since the description of the "symptoms" matches exactly (shape flying off the screen in the direction of the finger at the slightest movement, shape moved correctly at ACTION_UP event), I think the reason behind might be the same.
Basically the problem is in the update of the touch position coordinates within the ACTION_MOVE event. If you don't update the last touch position, the calculated distance will be always between the current touch position and the first touch position stored at ACTION_DOWN event: if you apply this offset consecutively to the path, the translation will sum up and consequently the shape will "fly" rapidly off the screen.
The solution is then quite simple: just update the last touch position at the end of the ACTION_MOVE event:
float mLastTouchX, mLastTouchY;
#Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN: {
// get touch position
final float x = ev.getX();
final float y = ev.getY();
// save the initial touch position
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_MOVE: {
// get touch position
final float x = ev.getX();
final float y = ev.getY();
// calculate the distance moved
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
// here apply translation to the path
// update touch position for the next move event
mLastTouchX = x;
mLastTouchY = y;
break;
}
}
return true;
}
Hope this helps.

Android + OpenCV: OnTouch getX(), getY() doesn't return right values

I am using OpenCV4Android FaceDetection sample. I have global table with rectangles of currently spoted faces:
private Rect[] facesArray;
also global floats to store onClick coordinates,
private float onClickX;
private float onClickY;
which are generated from onTouchEvent:
#Override
public boolean onTouchEvent(MotionEvent e) {
onClickX = e.getX();
onClickY = e.getY();
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
// something not important for this matter happens here
}
return super.onTouchEvent(e);
}
In onCameraFrame method before returning mat with view I am doing:
Core.circle(mRgba,new Point(onClickX,onClickY), 40, new Scalar(0, 255, 0, 255));
So what happens. I draw a small, green circle on coordinates that are fetched in onTouchEvent and sent to global variables. Those variables (onClickX, onClickY) are read by onCameraFrame and used for core.circle() function. My problem is that, circle isn't drawn precisely under my finger but always in lower-right place. This is what happens:
https://dl.dropboxusercontent.com/u/108321090/device-2013-07-24-004207.png
And this circle is always in the same direction/position to my finger whenever it is on screen, and meets it in top-left corner, dissapears on bottom right corner (goes outside screen). I tried using getRawX, geyRawY -> result is the same, I don't understeand why get commands doesn't return precise tap position but somewhere near it. I have no clue how to fix this.
This is the solution to your problem.
which is,
#Override
public boolean onTouch(View arg0,MotionEvent event) {
double cols = mRgba.cols();// mRgba is your image frame
double rows = mRgba.rows();
double xOffset = (mOpenCvCameraView.getWidth() - cols) / 2;
double yOffset = (mOpenCvCameraView.getHeight() - rows) / 2;
onClickX = (float)(event).getX() - xOffset;
onClickY = (float)(event).getY() - yOffset;

How to keep a dot drawn on canvas at a fixed point on screen, even when the canvas is pinch zoomed? - Android

I have a background image as a drawable in my custom view. This drawable may be pinch zoomed or moved.
Currently I need a green dot that is drawn on the image to be stationary relative to the screen. That is, it should be always at the same position with the pin as shown below. (Of course, the pin is simply an ImageView and does NOT move at all!)
I have successfully made it stationary relative to the screen, when the map behind is moved as follows in my custom view, MapView:
#Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(ev);
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: { // triggered as long as finger movers
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
// Only move if the ScaleGestureDetector isn't processing a gesture.
if (!mScaleDetector.isInProgress()) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
// update the starting point if the 'Start' button is not yet pressed
// to ensure the screen center (i.e. the pin) is always the starting point
if (!isStarted) {
Constant.setInitialX(Constant.INITIAL_X - dx);
Constant.setInitialY(Constant.INITIAL_Y - dy);
if ((historyXSeries.size() > 0) && (historyYSeries.size() > 0)) {
// new initial starting point
historyXSeries.set(0, Constant.INITIAL_X);
historyYSeries.set(0, Constant.INITIAL_Y);
}
}
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
break;
}
By doing that above, my green dot stays there, when the background image is moved.
But I have problems in trying to make it stay there, when the background image is zoomed.
Essentially, I don't really understand how canvas.scale(mScaleFactor, mScaleFactor) works, and therefore I cannot move the green dot accordingly like what I have done in the simple moving case.
I think something should be added in the scale listener handler below, could anybody help me fill that part?
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(1f, Math.min(mScaleFactor, 10.0f)); // 1 ~ 10
// HOW TO MOVE THE GREEN DOT HERE??
invalidate();
return true;
}
Or please at least explain how canvas.scale(mScaleFactor, mScaleFactor) works, and how may I move the green dot accordingly?
Keep in mind that the canvas is thought to scale everything according to the scale factor, so while going against the zoom is possible, it is probably not the best approach. However, if this is what you're looking for, I will help you as best as I can.
I am assuming the following:
Scale factor is relative to the current zoom (old zoom is always scale factor 1). If this is not the case, then you should observe the zoom values after scaling roughly 200% two times and seeing if the resulting scale factor is 4 or 3 (exponential or linear). You can achieve the results below by normalizing the scale factor to 2 for a zoom factor of 200%, for example. You'll have to remember the old scale factor in order to do so.
No rotation is performed
If this is the case then following can be said for a marker with respect to the zoom center.
For every horizonal pixel x away from the zoom center after zoom, its original position could be calculated to be: zoom_center_x + *x* / scale_factor (or alternatively zoom_center_x + (marker_x - zoom_center_x) / scale_factor). In other words, if zoom center is (50, 0) and the marker is (100, 0) with a scale factor of 2, then the x position of the marker prior to the zoom was 50 + (100 - 50) / 2 or 75. Obviously, if the marker is in the same position of the zoom center, then the x position will be the same as the zoom center. Similarly, if the scale is 1, then the x position for the marker will be the same as it is now.
The same can be applied to the y axis.
While I can't know exactly how to set the position of your marker, I would expect the code to look something like:
Point zoomCenter = detector.getZoomCenter();
// Set marker variable here
marker.setX(Math.round(zoomCenter.getX() + ((double)(marker.getX() - zoomCenter.getX())) / mScaleFactor));
marker.setY(Math.round(zoomCenter.getY() + ((double)(marker.getY() - zoomCenter.getY())) / mScaleFactor));
I hope that helps.

How to draw a dot on the zoomable bitmap by taking x, y coordinates form its image view

Please help me
I'm developing an android application to display a pinch zoom-able and drag-able bit map in a image view(Image view's boundaries are same as device screen size). I'm only zooming the bit map and show it in the image view
What I want is
When click a place on the device screen get that X an Y positions and draw a dot exactly in the place where I clicked on the bitmap
Its like marking your location on the bit map
Here is a descriptive image (Sorry I couldn't upload it to here because i have low reputation)
http://www4.picturepush.com/photo/a/9321682/img/9321682.jpg
as the image displays i want to get the x2, y2 positions of image view and draw the red dot in x1,y1 position on the bit map
It will be very help full for me if you can give me some codes or advice
thank you
I have gone through same problem and got resolved.
try this,
*Get touch points x, y *
int x = (int) event.getX();
int y = (int) event.getY();
2. Calculate inverse matrix
// calculate inverse matrix
Matrix inverse = new Matrix();
3. Get your ImageView matrix
getImageMatrix().invert(inverse);
4. Map your co-ordinates to actual image
// map touch point from ImageView to image
float[] touchPoint = new float[] { event.getX(), event.getY() };
inverse.mapPoints(touchPoint);
*4. Get your actual x, y of image *
x = (int) touchPoint[0];
y = (int) touchPoint[1];
In order to catch the coordinates of the touch, use the onTouch event of the imageView:
imageView.setOnTouchListener(this);
#Override
public boolean onTouch(View view, MotionEvent me) {
int x = me.getX();
int y = me.getY();
yourbitmap.setPixel(x + dx, y + dy, COLOR.RED);
return true;
}
where dx, dy is the displacement of the bitmap in the imageView.
edit
The values of dx and dy depend on your settings of the imageview, which you have set yourself. If the bitmap is always centered and not screen-filling, dy= (imageview_height - bitmap_height) / 2 and dx= (imageview_width - bitmap_width) / 2. If the image is scaled to fit in the imageview, you have to determine the scale factor. You can read about those computations here.

Canvas gamecamera - locationtranslate values messed up by applied canvas scaling

I'm working on an android game and am currently busy with a gamecamera type of component.
The best method seems to be translating the canvas (possibly with a matrix) to move the entire canvas to act like a gamecamera. (please correct me if I'm wrong).
this is all getting along pretty nicely, the gamecamera is moving and the x and y values of the camera are being used as guidelines to translate the canvas location:
public void onMove(float distanceX, float distanceY){
Camera.x += (int) distanceX;
Camera.y += (int) distanceY;
}
protected void onDraw(Canvas canvas) {
canvas.translate(Camera.x, Camera.y);
level.draw(canvas);
//the text is a ui component so shouldn't move with the level
canvas.restore();
canvas.drawText(fps + " fps", 0, 32, textPaint);
}
When trying to interact with the world I simply add the camera's coordinates to the touch's coordinates to get the correct point in the world:
public void onSingleTap(MotionEvent event) {
float mouseX = event.getX() - Camera.x;
float mouseY = event.getY() - Camera.y;
}
However, The user also has the choice to scale the screen, zooming to a point on the world.
case MotionEvent.ACTION_MOVE:
if (MODE == ZOOM && event.getPointerCount() == 2) {
float newDist = spacing(event);
if (newDist > 10f) {
Camera.zoom = newDist / PrevMultiDist;
//the midpoint for the zoom
midPoint(midpoint, event);
Camera.setPivot(midpoint.x, midpoint.y);
translateMatrix.postScale(Camera.zoom, Camera.zoom, Camera.pivotX, Camera.pivotY);
}
}
break;
The zoom is messing up the world-touch translation coordinates since the view is being moved to zoom on the chosen spot. I think if I substract the zoom offset value from the camera location that I will get the correct position in the world again.
So the touchevent becomes something like this:
public void onSingleTap(MotionEvent event) {
//ideally the zoomoffset might be added in the camera's location already?
float mouseX = event.getX() - Camera.x - zoomOffsetX;
float mouseY = event.getY() - Camera.y - zoomOffsetY;
}
I already found this question: Android get Bitmap Rect (left, top, right, bottom) on a Canvas but it doesn't provide the awsner I want and I can't figure out if his calculations can help me.
I feel like I'm missing something really simple. Hope someone can help!

Categories

Resources