I have an image that I rotate before I draw. The image is rotated by the angles of a hexagon. In other words, the image basically "highlights" the individual edges of a hexagon. I need to detect if the mouse was clicked inside of this rotated image. Detecting a mouse click inside of an unrotated image is pretty simple, but I have no idea about how to detect clicks within rotated points. Is there a way to get the points of the image's corners after rotation so I can place an invisible polygon on top of the image and use Polygon.contains()?
Image highlightEdge = new Image("assets/img/highlightEdge.png");
if(angle == 90){
highlightEdge.setCenterOfRotation(highlightEdge.getWidth(), 0);
highlightEdge.rotate(new Float(angle));
highlightEdge.draw(testPoint.x - 56, testPoint.y);
} else if(angle == 210) {
highlightEdge.setCenterOfRotation(0, 0);
highlightEdge.rotate(new Float(angle));
highlightEdge.draw(lastSettlement.x - 72, lastSettlement.y - 32);
} else if( angle == 330){
highlightEdge.setCenterOfRotation(0, 0);
highlightEdge.rotate(new Float(angle));
highlightEdge.draw(lastSettlement.x - 8, lastSettlement.y - 32);
} else if(angle == 30){
highlightEdge.setCenterOfRotation(0, 0);
highlightEdge.rotate(new Float(angle));
highlightEdge.draw(lastSettlement.x-8, lastSettlement.y);
} else if(angle == 150){
highlightEdge.setCenterOfRotation(0, 0);
highlightEdge.rotate(new Float(angle));
highlightEdge.draw(lastSettlement.x-72, lastSettlement.y);
} else {
highlightEdge.setCenterOfRotation(0, 0);
highlightEdge.rotate(new Float(angle));
highlightEdge.draw(lastSettlement.x-40, lastSettlement.y - 48);
}
You could create a Shape to exactly match the shape of the Image, and then use its method contains to detect if the mouse was clicked inside.
To take in consideration the rotation of the Image you could apply a corresponding rotation Transform to the Shape.
I created the method shapeFromImage that does this; it receives an Image and its position and returns the corresponding Shape:
/**
* Returns the Shape of an Image considering its rotation
* #param image
* #param x the x position of the Image
* #param y the y position of the Image
*/
public static Shape shapeFromImage(Image image, float x, float y) {
// create a rectangle with same position and size of the image
Shape imageShape = new Rectangle(x, y, image.getWidth(), image.getHeight());
// get the rotation angle of the image
float angle = image.getRotation();
// if the image is rotated, we also need to rotate our shape
if (angle != 0.f) {
// convert the rotation angle in radians to use in Transform
float angleInRadians = (float) Math.toRadians(angle);
// get the point of rotation to use in Transform.
// image.getCenterOfRotation returns a point relative to the image.
// for Transform we need an absolute point, so we add the image position to it
float rotationX = image.getCenterOfRotationX() + x;
float rotationY = image.getCenterOfRotationY() + y;
// create the rotation Transform to match the image rotation
Transform rotationTransform = Transform.createRotateTransform(angleInRadians, rotationX, rotationY);
// apply the rotation Transform to our shape
imageShape = imageShape.transform(rotationTransform);
}
return imageShape;
}
In your example you could use it like this:
float positionX;
float positionY;
if (angle == 90) {
highlightEdge.setCenterOfRotation(highlightEdge.getWidth(), 0);
highlightEdge.rotate(new Float(angle));
positionX = testPoint.x - 56;
positionY = testPoint.y;
highlightEdge.draw(positionX, positionY);
}
...
// you can now use this Shape to use its method "contains"
imageShape = shapeFromImage(highlightEdge, positionX, positionY);
Related
I'm trying to rotate a TextureRegion around its centre, however whenever I try to rotate it the rotation point is either the bottom corners of the Texture Region or a far part of the screen.
this is the update method in my object class(the Texture Region will be emulating the movements of this Object.
public void update(float delta) {
if (velocity.x < 0) {
rotation -= 50*delta;
if (rotation > 25) {
rotation = 25;
}
}
if (velocity.x > 0){
rotation += 50*delta;
if (rotation > 25) {
rotation = 25;
}
}
}
this where I call the draw method to bring the Texture Region to screen
batcher.draw(AssetLoader.saum, sam.getX(), (gameHeight - (gameHeight / 3)), -(sam.getWidth()), (gameHeight - (gameHeight / 3)), -(sam.getWidth()), -(sam.getWidth()), 1, 1, sam.getRotation());
Use one of the batch.draw methods that has originX and originY parameters.
originX and originY should be set to width/2 and height/2.
See methods here.
I am trying to move a triangle in the direction of the top vertex.
Depending on the rotation angle.
This is my code:
private static void render() {
// Clear the pixels on the screen and clear the contents of the depth buffer (3D contents of the scene)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Reset any translations the camera made last frame update
glLoadIdentity();
// Apply the camera position and orientation to the scene
//camera.applyTranslations();
glTranslated(0,0,-5);
glPushMatrix();
glRotated(f.get_direction(),0,0,1);
glTranslated(x,y,0);
f.draw();
glPopMatrix();
x+=(f.get_speed()/30)*cos(f.get_direction()+90);
y+=(f.get_speed()/30)*sin(f.get_direction()+90);
}
The point is that no matter what is the rotation angle that is the direction,
i want to move the triangle according to it.
Did you try to translate according to direction vector but there is simple problem cos and sin arguments is in radians glRotate in degrees:
so we must create static function
static double degToRad(double x)
{
return (x / 180.0) * Math.PI;
}
than use it
glLoadIdenity();
x += Math.cos(degToRad(getDirection() + 90)) * getSpeed();
y += Math.sin(degToRad(getDirection() + 90)) * getSpeed();
glTranslatef(x, y, 0);
glRotatef(getDirection(), 0, 0, 1);
drawObject();
you can also invert direction of movement just subsctacting current angle from 360:
glLoadIdenity();
x += Math.cos(degToRad(360 - getDirection() + 90)) * getSpeed();
y += Math.sin(degToRad(360 - getDirection() + 90)) * getSpeed();
glTranslatef(x, y, 0);
glRotatef(getDirection(), 0, 0, 1);
drawObject();
My goal is to have an image face wherever the mouse cursor is, here is my code for obtaining the rotation angle (within my Ship class) :
public void reOrient() {
Point m = MouseInfo.getPointerInfo().getLocation();
// getBoard().getPanel() is the JPanel on which the image will be drawn
Point c = getBoard().getPanel().getLocationOnScreen();
int x = m.x - c.x, y = m.y - c.y;
float angle = (float) (Math.atan2(getY() - y, x - getX()));
setOrientation(angle);
}
Then within my paint method on my JPanel :
AffineTransform reset = new AffineTransform();
reset.rotate(0, 0, 0);
g2d.rotate(ship.getOrientation(), ship.getX() + 26,
ship.getY() + 26);
g2d.drawImage(ship.getImage(), ship.getX(), ship.getY(), this);
g2d.setTransform(reset);
My issue is that when I move my mouse counterclockwise the image rotates clockwise...any ideas?
How to rotate the camera when using class VideoCapture on OpenCV? (Sample Face Detection on Android).
I'm rotating the canvas with:
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
{
Matrix matrix = new Matrix();
matrix.preTranslate(
(canvas.getWidth() - bmp.getWidth()) / 2,
(canvas.getHeight() - bmp.getHeight()) / 2);
matrix.postRotate(270f, (canvas.getWidth()) / 2,
(canvas.getHeight()) / 2);
canvas.drawBitmap(bmp, matrix, null);
}
but image from Camera doesn't rotate: Face Detect dont work.
The camera receives the stream from the following:
protected Bitmap processFrame(VideoCapture capture) {
capture.retrieve(mRgba, Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA);
capture.retrieve(mGray,
Highgui.CV_CAP_ANDROID_GREY_FRAME);
UPDATE
I did the following:
#Override
protected Bitmap processFrame(VideoCapture capture) {
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
Core.flip(mRgba.t(), mRgba, 0);
}
else {
}
capture.retrieve(mRgba, Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA);
capture.retrieve(mDetect_thread.mGray,
Highgui.CV_CAP_ANDROID_GREY_FRAME);
But is dont work. When I run the program in portret orientation(on android device)- program don't start When i run the rogram in landscape orientation - programm work, but when i rotation the device, program work, but image on display dont rotation
Your question is mostly a duplicate of this, except that you are looking for the Android version. It is quite similar but here it is, 90º rotation can be obtained by transposing and then flipping the image:
# rotate 90º counter-clockwise
Core.flip(mRgba.t(), mRgba, 0); //mRgba.t() is the transpose
# rotate 90º clockwise
Core.flip(mRgba.t(), mRgba, 1);
For other rotations you can use warpAffine:
Point center = new Point(x,y);
double angle = 90;
double scale = 1.0;
Mat mapMatrix = Imgproc.getRotationMatrix2D(center, angle, scale);
Imgproc.warpAffine(srcMat, dstMat, mapMatrix, Imgproc.INTER_LINEAR);
EDIT - more complete examples follow:
/**
* Image is first resized-to-fit the dst Mat and then rotated.
* mRgba is the source image, mIntermediateMat should have the same type.
*/
private void rotationTutorial(){
double ratio = mRgba.height() / (double) mRgba.width();
int rotatedHeight = mRgba.height();
int rotatedWidth = (int) Math.round(mRgba.height() * ratio);
Imgproc.resize(mRgba, mIntermediateMat, new Size(rotatedHeight, rotatedWidth));
Core.flip(mIntermediateMat.t(), mIntermediateMat, 0);
Mat ROI = mRgba.submat(0, mIntermediateMat.rows(), 0, mIntermediateMat.cols());
mIntermediateMat.copyTo(ROI);
}
/**
* Image is rotated - cropped-to-fit dst Mat.
*
*/
private void rotationAffineTutorial(){
// assuming source image's with and height are a pair value:
int centerX = Math.round(mRgba.width()/2);
int centerY = Math.round(mRgba.height()/2);
Point center = new Point(centerY,centerX);
double angle = 90;
double scale = 1.0;
double ratio = mRgba.height() / (double) mRgba.width();
int rotatedHeight = (int) Math.round(mRgba.height());
int rotatedWidth = (int) Math.round(mRgba.height() * ratio);
Mat mapMatrix = Imgproc.getRotationMatrix2D(center, angle, scale);
Size rotatedSize = new Size(rotatedWidth, rotatedHeight);
mIntermediateMat = new Mat(rotatedSize, mRgba.type());
Imgproc.warpAffine(mRgba, mIntermediateMat, mapMatrix, mIntermediateMat.size(), Imgproc.INTER_LINEAR);
Mat ROI = mRgba.submat(0, mIntermediateMat.rows(), 0, mIntermediateMat.cols());
mIntermediateMat.copyTo(ROI);
}
Note:
These examples might be orientation-specific, I made them for landscape orientation.
You should not call the code from these examples for every video frame. Some of the code should only run once.
If you only need to do 90, 180, or 270 degrees rotation (Which seems to be your case) you better use Core.flip() which is faster.
Here below a method that does it for you:
public static Mat rotate(Mat src, double angle)
{
Mat dst = new Mat();
if(angle == 180 || angle == -180) {
Core.flip(src, dst, -1);
} else if(angle == 90 || angle == -270) {
Core.flip(src.t(), dst, 1);
} else if(angle == 270 || angle == -90) {
Core.flip(src.t(), dst, 0);
}
return dst;
}
I am currently attempting to draw images on the screen at a regular rate like in a video game.
Unfortunately, because of the rate at which the image is moving, some frames are identical because the image has not yet moved a full pixel.
Is there a way to provide float values to Graphics2D for on-screen position to draw the image, rather than int values?
Initially here is what I had done:
BufferedImage srcImage = sprite.getImage ( );
Position imagePosition = ... ; //Defined elsewhere
g.drawImage ( srcImage, (int) imagePosition.getX(), (int) imagePosition.getY() );
This of course thresholds, so the picture doesn't move between pixels, but skips from one to the next.
The next method was to set the paint color to a texture instead and draw at a specified position. Unfortunately, this produced incorrect results that showed tiling rather than correct antialiasing.
g.setRenderingHint ( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
BufferedImage srcImage = sprite.getImage ( );
g.setPaint ( new TexturePaint ( srcImage, new Rectangle2D.Float ( 0, 0, srcImage.getWidth ( ), srcImage.getHeight ( ) ) ) );
AffineTransform xform = new AffineTransform ( );
xform.setToIdentity ( );
xform.translate ( onScreenPos.getX ( ), onScreenPos.getY ( ) );
g.transform ( xform );
g.fillRect(0, 0, srcImage.getWidth(), srcImage.getHeight());
What should I do to achieve the desired effect of subpixel rendering of an Image in Java?
You can use a BufferedImage and AffineTransform, draw to the buffered image, then draw the buffered image to the component in the paint event.
/* overrides the paint method */
#Override
public void paint(Graphics g) {
/* clear scene buffer */
g2d.clearRect(0, 0, (int)width, (int)height);
/* draw ball image to the memory image with transformed x/y double values */
AffineTransform t = new AffineTransform();
t.translate(ball.x, ball.y); // x/y set here, ball.x/y = double, ie: 10.33
t.scale(1, 1); // scale = 1
g2d.drawImage(image, t, null);
// draw the scene (double percision image) to the ui component
g.drawImage(scene, 0, 0, this);
}
Check my full example here: http://pastebin.com/hSAkYWqM
You can composite the image yourself using sub-pixel accuracy, but it's more work on your part. Simple bilinear interpolation should work well enough for a game. Below is psuedo-C++ code for doing it.
Normally, to draw a sprite at location (a,b), you'd do something like this:
for (x = a; x < a + sprite.width; x++)
{
for (y = b; y < b + sprite.height; y++)
{
*dstPixel = alphaBlend (*dstPixel, *spritePixel);
dstPixel++;
spritePixel++;
}
dstPixel += destLineDiff; // Move to start of next destination line
spritePixel += spriteLineDiff; // Move to start of next sprite line
}
To do sub-pixel rendering, you do the same loop, but account for the sub-pixel offset like so:
float xOffset = a - floor (a);
float yOffset = b - floor (b);
for (x = floor(a), spriteX = 0; x < floor(a) + sprite.width + 1; x++, spriteX++)
{
for (y = floor(b), spriteY = 0; y < floor (b) + sprite.height + 1; y++, spriteY++)
{
spriteInterp = bilinearInterp (sprite, spriteX + xOffset, spriteY + yOffset);
*dstPixel = alphaBlend (*dstPixel, spriteInterp);
dstPixel++;
spritePixel++;
}
dstPixel += destLineDiff; // Move to start of next destination line
spritePixel += spriteLineDiff; // Move to start of next sprite line
}
The bilinearInterp() function would look something like this:
Pixel bilinearInterp (Sprite* sprite, float x, float y)
{
// Interpolate the upper row of pixels
Pixel* topPtr = sprite->dataPtr + ((floor (y) + 1) * sprite->rowBytes) + floor(x) * sizeof (Pixel);
Pixel* bottomPtr = sprite->dataPtr + (floor (y) * sprite->rowBytes) + floor (x) * sizeof (Pixel);
float xOffset = x - floor (x);
float yOffset = y - floor (y);
Pixel top = *topPtr + ((*(topPtr + 1) - *topPtr) * xOffset;
Pixel bottom = *bottomPtr + ((*(bottomPtr + 1) - *bottomPtr) * xOffset;
return bottom + (top - bottom) * yOffset;
}
This should use no additional memory, but will take additional time to render.
I successfully solved my problem after doing something like lawrencealan proposed.
Originally, I had the following code, where g is transformed to a 16:9 coordinate system before the method is called:
private void drawStar(Graphics2D g, Star s) {
double radius = s.getRadius();
double x = s.getX() - radius;
double y = s.getY() - radius;
double width = radius*2;
double height = radius*2;
try {
BufferedImage image = ImageIO.read(this.getClass().getResource("/images/star.png"));
g.drawImage(image, (int)x, (int)y, (int)width, (int)height, this);
} catch (IOException ex) {
Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex);
}
}
However, as noted by the questioner Kaushik Shankar, turning the double positions into integers makes the image "jump" around, and turning the double dimensions into integers makes it scale "jumpy" (why the hell does g.drawImage not accept doubles?!). What I found working for me was the following:
private void drawStar(Graphics2D g, Star s) {
AffineTransform originalTransform = g.getTransform();
double radius = s.getRadius();
double x = s.getX() - radius;
double y = s.getY() - radius;
double width = radius*2;
double height = radius*2;
try {
BufferedImage image = ImageIO.read(this.getClass().getResource("/images/star.png"));
g.translate(x, y);
g.scale(width/image.getWidth(), height/image.getHeight());
g.drawImage(image, 0, 0, this);
} catch (IOException ex) {
Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex);
}
g.setTransform(originalTransform);
}
Seems like a stupid way of doing it though.
Change the resolution of your image accordingly, there's no such thing as a bitmap with sub-pixel coordinates, so basically what you can do is create an in memory image larger than what you want rendered to the screen, but allows you "sub-pixel" accuracy.
When you draw to the larger image in memory, you copy and resample that into the smaller render visible to the end user.
For example: a 100x100 image and it's 50x50 resized / resampled counterpart:
See: http://en.wikipedia.org/wiki/Resampling_%28bitmap%29