I have an app in which it has a main canvas and i have added another canvas of bitmap on top of it. In the main canvas I have eraser in which it will detect when the user touches the bitmap area. I want to know the x and y inside the bitmap where the eraser touches and so on while moving from main canvas since the bitmap canvas is different from the main canvas. I want the x and y of the main canvas be the same with the bitmap canvas to be move. Thanks
Here's my snippet
public void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
if(istouchingBitmap(x, y) == true){
float xRatio = (float)bitmap.getWidth() / parent.getWidth();
float yRatio = (float)bitmap.getHeight() / parent.getHeight();
float xPos = lastX * xRatio;
float yPos = lastY * yRatio;
eraseBitmap(bitmap, xPos , yPos , 5);
}
}
function to detect the bitmap when touch
/**
*
* #param x The X location of the cursor in main View.
* #param y The Y location of the cursor in main View.
* #return This is only used to detect if the cursor is touching the Bitmap Image.
*/
public boolean istouchingBitmap(float x, float y) {
matrix.invert(inverse);
float[] pts = {x, y};
inverse.mapPoints(pts);
if (!bounds.contains(pts[0], pts[1])) {
return false;
}
// copy the location
lastX = x;
lastY = y;
return Color.alpha(bitmap.getPixel((int) pts[0], (int) pts[1])) != 0;
}
You only need to get the X and Y values from the touch event.
Then sustract the top,left margin/offset values respectively.
The result is the X,Y coordinate relative to the start of the bitmap.
PD: When doing this, i was having problems handling the status bar height, since the way of measuring it changes between Android version and device model.
Hope this helps.
Finally :). This code will map the screen X and Y to the x and y inside the bitmap. Hope it help others. Good luck
float[] point = new float[] {lastX, lastY};
Matrix inverse = new Matrix();
matrix.invert(inverse);
inverse.mapPoints(point);
bitmapX = point[0];
bitmapY = point[1];
canvas.drawCircle(bitmapX, bitmapY, radius, mPaint); // punch a hole
Related
I've been trying to get the mouse coordinates from scaled canvas. The canvas.scale(scale, scale) gives me the correct coordinates but if i use canvas.scale(scale, scale, pivotX, pivotY) the coordinates are wrong.
//onDraw
canvas.scale(scaleFactor, scaleFactor); //default pivot x & y = 0, 0
//canvas.scale(scaleFactor, scaleFactor, pivotX, pivotY);
private float pixelWidth = 480;
private float pixelHeight = 320;
//onTouchEvent
touchX = (int) event.getX() * pixelWidth / getWidth();
touchX = (int) event.getY() * pixelHeight / getHeight();
worldX = touchX / scaleFactor;
worldY = touchY / scaleFactor;
What do i need to do to get the correct coordinates from canvas.scale with different pivot other than default 0, 0? I've read & tried about the matrix too but no luck.
solved it using matrix and this code from https://stackoverflow.com/a/20011005/7334711
Matrix matrix = new Matrix();
public void render(Canvas canvas) {
canvas.save();
matrix.setScale(mScaleFactor, mScaleFactor, pivotX, pivotY);
canvas.setMatrix(matrix);
...
float[] mv = new float[9];
#Override
public boolean onTouchEvent(MotionEvent event) {
// Get the values from the matrix into the float array
matrix.getValues(mv);
float touchX = (event.getX()*(1/mv[4]) - (mv[2]/mv[4]));
float touchY = (event.getY()*(1/mv[4]) - (mv[5]/mv[4]));
...
i have following problem. I have a bitmap and on this bitmap certain draw operations can be done. Like drawing a line, arrwow, etc.
This works pretty well, but the bitmap can be zoomed, dragged and rotated. For this a drawMatrix is used. Everything this works pretty good but, for drawing i need the original coordinates so that the drawings come in the right size, etc.
I can get this original coordinates with the following function. But it doesn't work with rotations. Does anybody have an idea, how to solve this?
/**
* When we have moved or zoomed we need the original image coordinates calculated from
* the screen coordinates. This function gives that back
*/
private Point translateScreenCoordsToOriginalImageCoords(MotionEvent e) {
float[] m = new float[9];
drawMatrix.getValues(m);
float transX = m[Matrix.MTRANS_X] * -1;
float transY = m[Matrix.MTRANS_Y] * -1;
float scaleX = m[Matrix.MSCALE_X];
float scaleY = m[Matrix.MSCALE_Y];
// TODO function does not work when rotated by 90 degree
if (scaleX == 0.0) {
scaleX = -1.0f;
scaleY = -1.0f;
}
int lastTouchX = (int) ((e.getX() + transX) / scaleX);
int lastTouchY = (int) ((e.getY() + transY) / scaleY);
lastTouchX = Math.abs(lastTouchX);
lastTouchY = Math.abs(lastTouchY);
return new Point(lastTouchX, lastTouchY);
}
This is how scaling is performed
/**
* Scale the canvas by
*
* #param scaleFactor e.g 2 is double
* #param focusX the center x point
* #param focusY the center y point
*
* #return true if scaling is performed
*/
private boolean doScale(float scaleFactor, int focusX, int focusY) {
if (this.scaleBoundaries(scaleFactor))
return false;
Matrix transformationMatrix = new Matrix();
// Zoom focus is where the fingers are centered
transformationMatrix.postTranslate(-focusX, -focusY);
transformationMatrix.postScale(scaleFactor, scaleFactor);
transformationMatrix.postTranslate(focusX, focusY);
drawMatrix.postConcat(transformationMatrix);
invalidate();
return true;
}
e.g. This is the draw function of a Line
#Override
public void draw(Canvas c, Matrix drawMatrix, boolean translateCoordiantes) {
float pts[] = new float[] {this.start.x, this.start.y, this.end.x, this.end.y};
if (translateCoordiantes)
drawMatrix.mapPoints(pts);
c.drawLine(pts[0], pts[1], pts[2], pts[3], this.paint);
}
Wow pskink thanks a lot! That was the answer! To get the orignal coordinates back, use:
private Point translateCoordinatesBack(MotionEvent event) {
Matrix inverse = new Matrix();
drawMatrix.invert(inverse);
float[] pts = {event.getX(), event.getY()};
inverse.mapPoints(pts);
return new Point((int) pts[0], (int) pts[1]);
}
I'm having a hard time implementing the rotation and movement of an on-screen OpenGL shape. Basically, what I want to achieve is being able to control the shape using a touch screen. Wherever I touch, it should rotate to that direction and start moving towards until it gets there.
Here's the code that sets up the frustum and the camera in onSurfaceChanged():
glViewport(0, 0, width, height);
float sizeRatio = (float) width / height;
Matrix.frustumM(
projectionMatrix, 0, -sizeRatio, sizeRatio, -1.0f, 1.0f, 1.0f, 20.0f
);
Matrix.setLookAtM(
viewMatrix, 0, 0, 0, -60, 0f, 0f, 0f, 0.0f, 1.0f, 0.0f
);
Matrix.multiplyMM(globalMvpMatrix, 0, projectionMatrix, 0, viewMatrix, 0);
and here's how the touch input is handled in onTouchEvent() (shape is an object that stores position and rotation and then draws the shape on the screen):
lastTouchX = event.getX();
lastTouchY = event.getY();
float shapePosX = shape.getPositionX();
float shapePosY = shape.getPositionY();
int[] viewport = new int[4];
glGetIntegerv(GL_VIEWPORT, viewport, 0);
float[] unprojectedTouchCoords = new float[4];
GLU.gluUnProject(
lastTouchX, lastTouchY, 0,
viewMatrix, 0,
projectionMatrix, 0,
viewport, 0,
unprojectedTouchCoords, 0
);
float unprojectedX = unprojectedTouchCoords[0] / unprojectedTouchCoords[3];
float unprojectedY = unprojectedTouchCoords[1] / unprojectedTouchCoords[3];
float rotation = 90 - (float) Math.toDegrees(Math.atan2(
shapePosY - unprojectedY, shapePosX - unprojectedX
));
shape.setRotation(rotation);
float moveX = 2 * (float) Math.cos(Math.toRadians(rotation));
float moveY = 2 * (float) Math.sin(Math.toRadians(rotation));
shape.move(moveX, moveY);
However, it doesn't seem to work well. The shape is moving in the wrong direction, and the rotation is only correct if the position of the shape is (0, 0). In any other case, it breaks.
I guess this problem involves distance between the camera and the shape in the OpenGL space, but I have no idea where and how to fix that. I tried bringing the camera closer to the shape but with no apparent improvement.
Can you please help me with this one? I'm getting really frustrated.
I haven't looked deeply at your code but I really want to give you a suggestion: the order of transformations matters in OpenGL, because they are cumulative.
This means that rotate and translate is different than translate and rotate.
Here is a good starting point to understand what I'm saying.
Seems like you have co-ordinates calculation problem. You need to #Override the onTouchEvent method of GLSurfaceView, rest is below
private final float TOUCH_SCALE_FACTOR = 180.0f / 320;
private float mPreviousX;
private float mPreviousY;
#Override
public boolean onTouchEvent(MotionEvent e) {
// MotionEvent reports input details from the touch screen
// and other input controls. In this case, you are only
// interested in events where the touch position changed.
float x = e.getX();
float y = e.getY();
switch (e.getAction()) {
case MotionEvent.ACTION_MOVE:
float dx = x - mPreviousX;
float dy = y - mPreviousY;
// reverse direction of rotation above the mid-line
if (y > getHeight() / 2) {
dx = dx * -1 ;
}
// reverse direction of rotation to left of the mid-line
if (x < getWidth() / 2) {
dy = dy * -1 ;
}
mRenderer.setAngle(
mRenderer.getAngle() +
((dx + dy) * TOUCH_SCALE_FACTOR)); // = 180.0f / 320
requestRender();
}
mPreviousX = x;
mPreviousY = y;
return true;
}
i am translating and scaling image by matrix, now i have matrix values . so from matrix value i want to convert my touch coordinates to its position which is getting by matrix. so what should i do for that ?
please help me asap.
private void drawPoint(float x, float y, float pressure, float width) {
// left is tx of matrix
// top is ty of matrix
float curX = (x - left) / (scale * scale);
float curY = (y - top) / (scale * scale);
canvas.drawPoint((curX - left), (curY - top) , mPaint);
}
I know it's an old post, but I had the same problem.
I have an image which is applied a matrix transformation, first I resize it, and then I do a translation.
image = new Matrix();
image.setScale(zoom, zoom);
Paint drawPaint = new Paint();
drawPaint.setAntiAlias(true);
drawPaint.setFilterBitmap(true);
float centerScaledWidth = image_center.x * zoom / 2;
float centerScaledHeigth = image_center.y * zoom / 2;
image.postTranslate(screen_center.x - centerScaledWidth,
screen_center.y - centerScaledHeigth);
canvas.drawBitmap(bmp, image, drawPaint);
To get the points touched on the image I get trace of the matrix image and this is the method:
#Override
public boolean onTouchEvent(MotionEvent 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();
float[] pts = {x, y};
Matrix m = new Matrix();
// This is the magic, I set the new matrix m
// as the inverse of
// the matrix applied to the image
image.invert(m);
// transform the points using the inverse matrix
m.mapPoints(pts);
// do what you have to do
.......
Log.i("transformed", pts[0] +" "+pts[1]);
break;
}
}
return super.onTouchEvent(ev);
}
For Bitmaps drawn with simple (x,y) coordinates,
float _x = x - (bitmap.getWidth() / 2);
float _y = y - (bitmap.getHeight() / 2);
canvas.drawBitmap(bitmap, _x, _y, null);
I can detect if the bitmap has been touched.
I'm drawing a bitmap to the screen with
dest = new Rect(0,0,0,0);
src = new Rect(0,0,0,0);
mSpriteHeight = (int) (sprite_pixel_height * mScale + 0.5f);
mSpriteWidth = (int) (sprite_pixel_width * mScale + 0.5f);
src.top = 0;
src.left = 0;
src.bottom = mSpriteHeight;
src.right = mSpriteWidth;
dest.top = (int) (_x * mScale);
dest.bottom = (int) ((_x + mSpriteHeight) * mScale);
dest.left = (int) (_y * mScale);
dest.right = (int) ((_y + mSpriteWidth) * mScale);
canvas.drawBitmap(bitmap, src, dest, null);
trying to incorporate the
screen density because "This function ignores the density associated with the bitmap. ... so must already have the appropriate scaling factor applied."
I haven't been able to detect touches to the translated bitmaps. I must need to do a similar translation using mScale, but I'm lost.
Is there a better way to define the src and dest in my original canvas.drawBitmap(bitmap, src, dest, null);?
Anyone know an example where this has been done? I can't seem to find the right search terms to find such an example.
It seems to me that the same method applies as for simple x,y coordinates. You simply need to use the coordinates and size of Rect dest to calculate whether the bitmap has been touched.
In other words, you need to do something like this:
public boolean picked(Rect dest, int touchX, int touchY) {
if(touchX > dest.left && touchX < dest.left + dest.width() &&
touchY > dest.top && touchY < dest.top + dest.height())
return true;
return false;
}