I am trying to apply a transformation on a rectangle within another rectangle.... and having quite a bit of difficulty. Here is an example of what I am trying to achieve, the rotation will always be in increments of 90 degrees:
I have the bottom left X/Y, width, and height of both the outer and inner rectangles.... I'm trying to calculate these same values for the transformed inner rectangle.
My attempt can be found below, I tried rotating all 4 corners around the center of the large rect then put them back together as a rect. This may not work because the large rect width/height changes during the rotation. Does anyone know of a formula to accomplish this? If someone could point me to some good resource that would be fantastic.
My Code:
Vector2 center = new Vector2(largeRectWidth / 2.0f, largeRectHeight / 2.0f);
Rect innerRectRotated = RotateRectangleAroundPivot(innerRect, center, this.Rotation);
public static Rect RotateRectangleAroundPivot(Rect rect,
Vector2 pivot,
float rotation)
{
Vector2 leftTop = new Vector2(rect.x, rect.y + rect.height);
Vector2 rightTop = new Vector2(rect.x + rect.width, rect.y + rect.height);
Vector2 leftBottom = new Vector2(rect.x, rect.y);
Vector2 rightBottom = new Vector2(rect.x + rect.width, rect.y);
leftTop = RotatePointAroundPivot(leftTop, pivot, rotation);
rightTop = RotatePointAroundPivot(rightTop, pivot, rotation);
leftBottom = RotatePointAroundPivot(leftBottom, pivot, rotation);
rightBottom = RotatePointAroundPivot(rightBottom, pivot, rotation);
Vector2 min = Vector2.Min(Vector2.Min(leftTop, rightTop),
Vector2.Min(leftBottom, rightBottom));
Vector2 max = Vector2.Max(Vector2.Max(leftTop, rightTop),
Vector2.Max(leftBottom, rightBottom));
return new Rect(min.x, min.y, (max.x - min.x), (max.y - min.y));
}
public static Vector2 RotatePointAroundPivot(Vector2 point, Vector2 pivot, float angle)
{
angle = angle * Mathf.PI / 180.0f;
return new Vector2((float)(Math.Cos(angle) * (point.x - pivot.x) - Math.Sin(angle) * (point.y - pivot.y) + pivot.x), (float)(Math.Sin(angle) * (point.x - pivot.x) + Math.Cos(angle) * (point.y - pivot.y) + pivot.y));
}
well in 2D for 90deg rotations you do not need any trigonometry
use this instead:
input point (x,y) is rotated by 90deg like this:
xx=y; yy=-x;
the other direction is:
xx=-y; yy=x;
which formula is CW and CCW depends on your coordinate system
booth rotates point around point (0,0) by 90deg
so if you want to rotate by any other point just shift the points before and after ...
so do it like this:
translate point to your center of rotation
x-=x0; y-=y0;
rotate left or right
for example:
aa=x; bb=y; x=-bb; y=+aa;
translate point back
x+=x0; y+=y0;
now the x,y holds the rotated point
[notes]
if you want the center of rotation to be the middle of rectangle
then the x0,y0 is average point ...
so if rectangle is defined as x1,y1,...,x4,y4
then x0=(x1+x2+x3+x4)/4.0; and y0=(y1+y2+y3+y4)/4.0;
Related
I am not well versed in matrix math so any help would be appreciated. So I have a simple ship sprite that rotates and moves. In order to calculate the bounding rectangle I tried using a rotation matrix and the code I got off of these tutorials:
http://xbox.create.msdn.com/en-US/education/catalog/tutorial/collision_2d_perpixel
But whenever the sprite rotates it's X and Y position of the bounding rectangle change drastically, by like 100s of pixels. The method for calculating the new bounding rectangle is as follows:
public static Rectangle CalculateBoundingRectangle(Rectangle rectangle, Matrix transform)
{
// Get all four corners in local space
Vector2 leftTop = new Vector2(rectangle.Left, rectangle.Top);
Vector2 rightTop = new Vector2(rectangle.Right, rectangle.Top);
Vector2 leftBottom = new Vector2(rectangle.Left, rectangle.Bottom);
Vector2 rightBottom = new Vector2(rectangle.Right, rectangle.Bottom);
// Transform all four corners into work space
Vector2.Transform(ref leftTop, ref transform, out leftTop);
Vector2.Transform(ref rightTop, ref transform, out rightTop);
Vector2.Transform(ref leftBottom, ref transform, out leftBottom);
Vector2.Transform(ref rightBottom, ref transform, out rightBottom);
// Find the minimum and maximum extents of the rectangle in world space
Vector2 min = Vector2.Min(Vector2.Min(leftTop, rightTop),
Vector2.Min(leftBottom, rightBottom));
Vector2 max = Vector2.Max(Vector2.Max(leftTop, rightTop),
Vector2.Max(leftBottom, rightBottom));
// Return as a rectangle
return new Rectangle((int)min.X, (int)min.Y,
(int)(max.X - min.X), (int)(max.Y - min.Y));
}
I am assuming this works since it comes from the Microsoft site and all. So I think the problem is with my matrices. I thought that If I only used a rotation matrix it would only spin the thing; not change its X and Y coordinates drastically. right now this is what I have:
// calculate transformation
Matrix transformation = Matrix.CreateRotationZ((float)rotation) * Matrix.CreateRotationZ((float)rotation);;
//update bounding rectangle
rectangle = new Rectangle((int)(position.X - origin.X), (int)(position.Y - origin.Y), texture.Width, texture.Height);
rectangle = BoundingAndCollision.CalculateBoundingRectangle(rectangle, transformation);
I tried a variety of other things including without the origin matrix and different orders of them. I also tried what a tutorial said was a general one that would work for anything, but that yielded the basically the same results:
Matrix transformation = Matrix.CreateTranslation(new Vector3(origin, 0.0f)) *
Matrix.CreateRotationZ((float)rotation) *
Matrix.CreateScale(scale) *
Matrix.CreateTranslation(position.X, position.Y, 0);
If that is not clear enough I can post screenshots, just let me know.Thank you in advance for the help!
A rotation matrix rotates around 0,0 and your rectangle is already placed in the world.
To solve first subtract the rectangle's center from each vertex (translate the rectangle to be centered at 0,0) and then add it again after rotating (place again on original location). In the code im assuming Y goes from top to bottom:
public static Rectangle CalculateBoundingRectangle(Rectangle rectangle, Matrix transform)
{
Vector2 center = new Vector2(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2);
// Get all four corners in local space
Vector2 leftTop = new Vector2(rectangle.Left, rectangle.Top) - center;
Vector2 rightTop = new Vector2(rectangle.Right, rectangle.Top) - center;
Vector2 leftBottom = new Vector2(rectangle.Left, rectangle.Bottom) - center;
Vector2 rightBottom = new Vector2(rectangle.Right, rectangle.Bottom) - center;
// Transform all four corners into work space
Vector2.Transform(ref leftTop, ref transform, out leftTop);
Vector2.Transform(ref rightTop, ref transform, out rightTop);
Vector2.Transform(ref leftBottom, ref transform, out leftBottom);
Vector2.Transform(ref rightBottom, ref transform, out rightBottom);
leftTop += center;
rightTop += center;
leftBottom += center;
rightBottom += center;
// Find the minimum and maximum extents of the rectangle in world space
Vector2 min = Vector2.Min(Vector2.Min(leftTop, rightTop),
Vector2.Min(leftBottom, rightBottom));
Vector2 max = Vector2.Max(Vector2.Max(leftTop, rightTop),
Vector2.Max(leftBottom, rightBottom));
// Return as a rectangle
return new Rectangle((int)min.X, (int)min.Y,
(int)(max.X - min.X), (int)(max.Y - min.Y));
}
Yo, Hi everybody
Is there any Way to draw a circle? I don't want to use a Texture/sprite to draw a circle Because the Player is the Circle so the Circle Should move ... and also I'm Trying to make it so the Player/Circle's Size gets bigger and bigger When he eats some food blablabla...
anyways, if anybody knows how to do it please Tell me.
OTHERWISE : IS THERE A WAY TO CHANGE A TEXTURE HEIGHT / WIDTH , THEN I WILL MAKE A SIMPLE CIRCLE TEXTURE AND CHANGE HEIGHT / WIDTH OF IT.
Thanks.
You can use 3D primitives like 'Someone' :> already posted or use the C3.XNA.Primitives2D libary where you can use a extension for SpriteBatch to draw a circle
public static void DrawCircle(this SpriteBatch spriteBatch, Vector2 center, float radius, int sides, Color color, float thickness);
If you use the same value for radius and thickness the circle appears filled.
I didn't find the offizial download link, but there are also uploads at sourceforge.
Also you can generate a circle dynamically via code like:
public static Texture2D GenerateCircleTexture(GraphicsDevice graphicsDevice, int radius, Color color, float sharpness)
{
int diameter = radius * 2;
Texture2D circleTexture = new Texture2D(graphicsDevice, diameter, diameter, false, SurfaceFormat.Color);
Color[] colorData = new Color[circleTexture.Width * circleTexture.Height];
Vector2 center = new Vector2(radius);
for (int colIndex = 0; colIndex < circleTexture.Width; colIndex++)
{
for (int rowIndex = 0; rowIndex < circleTexture.Height; rowIndex++)
{
Vector2 position = new Vector2(colIndex, rowIndex);
float distance = Vector2.Distance(center, position);
// hermite iterpolation
float x = distance / diameter;
float edge0 = (radius * sharpness) / (float)diameter;
float edge1 = radius / (float)diameter;
float temp = MathHelper.Clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
float result = temp * temp * (3.0f - 2.0f * temp);
colorData[rowIndex * circleTexture.Width + colIndex] = color * (1f - result);
}
}
circleTexture.SetData<Color>(colorData);
return circleTexture;
}
Sharpness below 1f blurs the circle.
The only way to draw primitives (e.g. circles) is in 3D:
https://msdn.microsoft.com/en-us/library/bb196414.aspx
Or you can load a texture that is 1*1, stretch it into a line and then use a bunch of those lines to make a circle.
Use:
public void Draw (
Texture2D texture,
Vector2 position,
Nullable<Rectangle> sourceRectangle,
Color color,
float rotation,
Vector2 origin,
Vector2 scale,
SpriteEffects effects,
float layerDepth
)
to stretch the texture.
Or you can just use a circle texture and stretch it.
If you are making something like agar.io then you might want to use a texture combined with the circle primitive so you can make the circle 'wobbly'.
I am working on an application for the past few weeks which involves some trigonometry and am currently stuck. As shown in the diagram below, I have a circular item (green circle at position #1) which I know the center point (let's call that X1,Y1). The circle has another point (orange circle) that is off-centered a bit - midway between two other marks (blue circles). These marks can move around. The coordinates of the orange point are calculated (let's call it X2, Y2) and the angle of the blue line is calculated (call it Angle) in relation to the horizontal of the circle.
I can calculate the difference between the center of the circle and the point by:
deltaX = X2-X1
deltaY = Y2-Y1
I need to move and rotate the green circle (either CW or CCW - whichever is shorter) from it's start location (position 1) over to position 2. This means the angle could be negative or positive. The blue line must end up vertical and the orange dot at the center of position 2 (red square). I know the coordinates for the center of position 2 (let's call this point X3,Y3). Position #1 and position #2 are exactly 90 degrees from each other.
I thought I could use some trig identity formulas that calculate the rotation of a point, as such:
offsetX = deltaX * cos(90-Angle) - deltaY * sin(90-Angle)
offsetY = deltaX * sin(90-Angle) + deltaY * cos(90-Angle)
I was hoping these offsets would be what I need to adjust the circle to it's new center when it moves/rotates over to position 2.
X3 = X3 + offsetX
Y3 = Y3 + offsetY
However, when I try use this math, it's not placing the orange mark of the circle in the center of the square. Not sure if my equations and calculations are correct based on the angle of rotation (positive or negative, CW or CCW) or if I'm using the angle correctly (where I subtract the known angle from 90 degrees). How do I correctly calculate the final point/position? Any help and examples would be greatly appreciated!
Thank you very much for your time!
So you need to rotate your circle by 90 - Angle and then move orange point to (X3, Y3)?
First you need to find orange point coordinate after rotation:
newX = X2 * cos(90 - Angle) - Y2 * sin(90 - Angle);
newY = X2 * sin(90 - Angle) + Y2 * cos(90 - Angle);
newX and newY are orange point coordinates after rotation. To find move transformation simply substract:
moveX = X3 - newX;
moveY = Y3 - newY;
Now if you rotate circle by 90 - Angle and move it by (moveX, moveY) orange point will move to (X3, Y3). That is if you rotate circle around (0, 0) point. If you rotating around some (X, Y) point, you first need to substract X from X2, Y from Y2 and then add X to newX, Y to newY. That substraction 'moves' your rotation base point to (0, 0), so after rotation you need to move it back:
newX = (X2 - X) * cos(90 - Angle) - (Y2 - Y) * sin(90 - Angle) + X;
newY = (X2 - X) * sin(90 - Angle) + (Y2 - Y) * cos(90 - Angle) + Y;
Be aware that your code is using a counter clockwise rotation, (conventionally angles are measured counter clockwise) which may be why you're not getting the results you expect. if you want a clockwise rotation try:
offsetX = deltaX * cos(angle) + deltaY * sin(angle)
offsetY = -deltaX * sin(angle) + deltaY * cos(angle)
Ensure your angles are in radians not degrees.
Drawing some lines may help you debug things too.
I've been trying to combine these two samples from David Amador:
http://www.david-amador.com/2010/03/xna-2d-independent-resolution-rendering/
http://www.david-amador.com/2009/10/xna-camera-2d-with-zoom-and-rotation/
Everything is working fine except I'm having some difficulty getting the mouse coordinates. I was able to get them for each individual sample, but my math for taking both into account seems to be wrong.
The mouse coordinates ARE correct if my virtual resolution and normal resolution are the same. It's when I do something like Resolution.SetVirtualResolution(1920, 1080)
and Resolution.SetResolution(1280, 720, false) when the coordinates slowly get out of sync as I move the camera.
Here is the code:
public static Vector2 MousePositionCamera(Camera camera)
{
float MouseWorldX = (Mouse.GetState().X - Resolution.VirtualWidth * 0.5f + (camera.position.X) * (float)Math.Pow(camera.Zoom, 1)) /
(float)Math.Pow(camera.Zoom, 1);
float MouseWorldY = ((Mouse.GetState().Y - Resolution.VirtualHeight * 0.5f + (camera.position.Y) * (float)Math.Pow(camera.Zoom, 1))) /
(float)Math.Pow(camera.Zoom, 1);
Vector2 mousePosition = new Vector2(MouseWorldX, MouseWorldY);
Vector2 virtualViewport = new Vector2(Resolution.VirtualViewportX, Resolution.VirtualViewportY);
mousePosition = Vector2.Transform(mousePosition - virtualViewport, Matrix.Invert(Resolution.getTransformationMatrix()));
return mousePosition;
}
In resolution I added this:
virtualViewportX = (_Device.PreferredBackBufferWidth / 2) - (width / 2);
virtualViewportY = (_Device.PreferredBackBufferHeight / 2) - (height / 2);
Everything else is the same as the sample code. Thanks in advance!
Thanks to David Gouveia I was able to identify the problem... My camera matrix was using the wrong math.
I'm going to post all of my code with the hopes of helping someone who is trying to do something similar.
Camera transformation matrix:
public Matrix GetTransformMatrix()
{
transform = Matrix.CreateTranslation(new Vector3(-position.X, -position.Y, 0)) * Matrix.CreateRotationZ(rotation) *
Matrix.CreateScale(new Vector3(Zoom, Zoom, 1)) * Matrix.CreateTranslation(new Vector3(Resolution.VirtualWidth
* 0.5f, Resolution.VirtualHeight * 0.5f, 0));
return transform;
}
That will also center the camera. Here's how you get the mouse coordinates combining both the Resolution class and camera class:
public static Vector2 MousePositionCamera(Camera camera)
{
Vector2 mousePosition;
mousePosition.X = Mouse.GetState().X;
mousePosition.Y = Mouse.GetState().Y;
//Adjust for resolutions like 800 x 600 that are letter boxed on the Y:
mousePosition.Y -= Resolution.VirtualViewportY;
Vector2 screenPosition = Vector2.Transform(mousePosition, Matrix.Invert(Resolution.getTransformationMatrix()));
Vector2 worldPosition = Vector2.Transform(screenPosition, Matrix.Invert(camera.GetTransformMatrix()));
return worldPosition;
}
Combined with all of the other code I posted/mentioned this should be all you need to achieve resolution independence and an awesome camera!
Here's how I draw some shape defined by vertices not shown here.
Vector3 position = (5,5,1);
Matrix world = Matrix.CreateTranslation(position);
BasicEffect basicEffect = new BasicEffect(graphicsDevice);
Matrix view = Matrix.CreateLookAt(new Vector3(0, 0, -20), new Vector3(0, 0, 100), Vector3.Up);
Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
graphics.Viewport.AspectRatio,
1.0f,
100);
// Set BasicEffect parameters.
basicEffect.World = world;
basicEffect.View = view;
basicEffect.Projection = projection;
//....draw some shape with basicEffect
I would like to paint the same shape only farther away so that its center stays in the same (x,y) pixel on screen but it is overall smaller as it's more distant.
I've tried scaling the position vector but had no success with it:
position .Z *= 2;
position .X *= 2;
position .Y *= 2;
What's the right way to do this?
Think about it geometrically: moving the object away from the camera means moving it along a line defined by two points: the camera's position and the object's position.
Now it's easy!
1) Find the vector object-to-camera, i.e.
Vector3 direction = objectPosition - cameraPosition;
2) Move the object alongside that vector by a certain amount, that is:
2.1) Normalize the direction
direction.Normalize();
2.2) Move the object by an amount x in that direction
objectPosition += direction * x;
And there you have it.