I have four coordinates: x,y,width=w,height=h and I have two rectangles with the following coordinates:
r1.x=2,r1.y=3,r1.w=5,r1.h=6;
r2.x=0, r2.y=7,r2.w=-4,r4.h=2
How you can observe this intersection is empty.
what I did until now it was:
rectangle intersection (rectangle r1, rectangle r2){
r1.x=max(r1.x,r2.x);
r1.y=max(r1.y,r2.y);
r1.w=min(r1.w,r2.w);
r1.h=min(r1.h,r2.h);
return r1;
}
I think the above code it is used when there is an intersection, but when the intersection is empty I do not know. Also, I would like to print a message "empty" when there is no intersection.
thanks!
The method you are using for rectangle intersection does NOT work when rectangles are represented with their width and height.
It could work if you store the rectangles' two opposite corners (instead of one corner and the dimensions) and make sure that the first corner's coordinates are always less than or equal to the second corner, effectively storing min_x, min_y, max_x, and max_y for your rectangles.
I would suggest that you adopt the convention of making sure the rectangles always include their min coordinates and always exclude their max coords.
Assuming you have something not very different from:
struct rectangle {
int x;
int y;
int w;
int h;
};
(or the same using float or double instead of int)
I will assume here that w and h are always positive, if they may be negative, you should first normalize the input rectangle to ensure that they are.
You find the intersection by finding its opposite corners, and ensuring that lower left come before upper right:
rectangle intersection(const rectangle& r1, const rectangle& r2) {
// optionaly control arguments:
if (r1.w < 0 || r1.h < 0 || r2.w < 0 || r2.h < 0) {
throw std::domain_error("Unnormalized rectangles on input");
}
int lowx = max(r1.x, r2.x); // Ok, x coordinate of lower left corner
int lowy = max(r1.y, r2.y); // same for y coordinate
int upx = min(r1.x + r1.w, r2.x + r2.w) // x for upper right corner
int upy = min(r1.y + r1.h, r2.y + r2.h) // y for upper right corner
if (upx < lowx || upy < lowy) { // empty intersection
throw std::domain_error("Empty intersection");
}
return rectangle(lowx, lowy, upx - lowx, upy - lowy);
}
You can normalize a rectangle by forcing positive values for width and height:
rectangle& normalize(rectangle& r) {
if (r.w < 0) {
r.x += r.w;
r.w = - r.w;
}
if (r.h < 0) {
r.y += r.h;
r.h = -r.h;
}
return r;
}
You can then use that in a second function to display the intersection result:
void display_intersection(std::outstream out, rectangle r1, rectangle r2) {
try {
rectangle inter = intersection(normalize(r1), normalize(r2));
out << "(" << inter.x << ", " << inter.y << ") to (";
out << inter.x + inter.w << ", " << inter.y + inter.h << ")" << std::endl;
}
except (std::domain_error& e) {
out << "empty" << std::endl;
}
}
Related
I am currently working on some basic 2D RigidBody Physics and have run into an issue. I have a function that checks for collision between a Circle and a AABB but sometimes the Circle (in this case the player) will collide then disappear and if I print out the position when this happens I just set "nan".
bool Game::Physics::RigidBody2D::CircleAABB(RigidBody2D& body)
{
sf::Vector2f diff = m_Position - body.m_Position;
sf::Vector2f halfExtents = sf::Vector2f(body.m_Size.x / 2.0f, body.m_Size.y / 2.0f);
sf::Vector2f diffContrained = diff;
if (diff.x > halfExtents.x)
{
diffContrained.x = halfExtents.x;
}
else if (diff.x < -halfExtents.x)
{
diffContrained.x = -halfExtents.x;
}
if (diff.y > halfExtents.y)
{
diffContrained.y = halfExtents.y;
}
else if (diff.y < -halfExtents.y)
{
diffContrained.y = -halfExtents.y;
}
sf::Vector2f colCheck = diff - diffContrained;
sf::Vector2f VDirNorm = NormVector(colCheck);
sf::Vector2f colToPlayer = NormVector(m_Position - (diffContrained + body.m_Position));
float dist = getMagnitude(colCheck) - m_fRadius;
//std::cout << dist << std::endl;
if (dist < 0)
{
OnCollision((diffContrained + body.m_Position) - m_Position);
m_Position += (VDirNorm * abs(dist));
body.m_Position -= (VDirNorm * abs(dist))* (1.0f - body.m_fMass);
return true; //Collision has happened
}
return false;
}
This happens randomly and with almost no clear reason although it seems to happen more often when the circle is moving fast but can happen as well when it is moving slowly or one or two times when it is not moving at all.
An added note is that I apply gravity to the Y velocity and on collision set the velocity of the coordinating axis to 0.
So my question is, is something clearly wrong here to those with more physics experience than me?
Note: Using SFML for drawing and Vector2 class physics code is all mine.
EDIT: The OnCollision function checks the side the collision so that objects that inherit can use this (e.g. check if the collision was below to trigger a "isGrounded" boolean). In the this case the player checks the side and then sets the velocity on that axis to 0 and also trigger a isGrounded boolean when it is below.
void Game::GamePlay::PlayerController::OnCollision(sf::Vector2f vDir)
{
if (abs(vDir.x) > abs(vDir.y))
{
if (vDir.x > 0.0f)
{
//std::cout << "Right" << std::endl;
//Collision on the right
m_Velocity.x = 0.0f;
}
if (vDir.x < 0.0f)
{
//std::cout << "Left" << std::endl;
//Collision on the left
m_Velocity.x = 0.0f;
}
return;
}
else
{
if (vDir.y > 0.0f)
{
//std::cout << "Below" << std::endl;
//Collision below
m_Velocity.y = 0.0f;
if (!m_bCanJump && m_RecentlyCollidedNode != nullptr)
{
m_RecentlyCollidedNode->ys += 3.f;
}
m_bCanJump = true;
}
if (vDir.y < 0.0f)
{
//std::cout << "Above" << std::endl;
//Collision above
m_Velocity.y = 0.0f;
}
}
}
From debugging out velocity and position no real reason has come to the surface.
inline sf::Vector2f NormVector(sf::Vector2f vec)
{
float mag = getMagnitude(vec);
return vec / mag;
}
Solution:
if (colCheck.x == 0 && colCheck.y == 0)
{
std::cout << "Zero Vector" << std::endl;
float impulse = m_Velocity.x + m_Velocity.y;
m_Velocity.x = 0;
m_Velocity.y = 0;
m_Velocity += NormVector(diff)*impulse;
}
else
{
VDirNorm = NormVector(colCheck);
dist = getMagnitude(colCheck) - m_fRadius;
}
One issue I see is NormVector with a zero vector. You'll divide by zero, generating NaNs in your returned vector. This can happen in your existing code when diff and diffContrained are the same, so colCheck will be (0,0) causing VDirNorm to have NaNs in it, which will propagate into m_position.
Typically, a normalized zero length vector should stay a zero length vector (see this post), but in this case, since you're using the normalized vector to offset your bodies after the collision, you'll need to add code to handle it in a reasonable fashion.
How do I find the area of square or rectangle assuming user has entered some accurate points to form a square or rectangle.
I need to calculate the area inside the square class and rectangle class respectively.
I have 2 vector of coordinates, coordX and coordY.
My idea is when either or x or y has same value it will be a line and I can find the distance x2 but I'm not sure how to implement it in code.
double Square::computeArea() {
double area;
for (int x = 0; x < coordX.size; x++) {
for (int y = 0; y < coordY.size; y++) {
if (coordX[x] == coordY[y])
{
//....
}
}
}
return area;
}
This is how i populate my vector with user input
Square Square;
for ( int i = 1; i <= 4; i++) {
cout << "Please enter x-coordinate of pt " << i << ": ";
cin >> x;
Square.setXCoordinate(x);
cout << "Please enter y-coordinate of pt " << i << ": ";
cin >> y;
Square.setYCoordinate(y);
}
this is my mutator function in my class. Square inherit from ShapeTwoD
void ShapeTwoD::setXCoordinate(int x) {
coordX.push_back(x);
}
void ShapeTwoD::setYCoordinate(int y) {
coordY.push_back(y);
}
No need for square root.
Take two edges from one vertex, rotate one by 90°, take dot product.
double dx1 = coordX[3] - coordX[0];
double dy1 = coordY[3] - coordY[0];
double dx2 = coordX[1] - coordX[0];
double dy2 = coordY[1] - coordY[0];
double area = abs(dx1*dy2 - dy1*dx2)
As a bonus, this will calculate the correct area for all parallelograms, not just rectangles.
This assumes, the points are entered in clockwise or couter-clockwise order. If that's not the case, find out which point has the greatest distance to point[0] then discard it and use the other two instead of 1 and 3 above.
Assuming your coordinates are something like
// 3-----------2
// | |
// | |
// 0-----------1
Then you could do
#include <cmath>
double distance(double x1, double x2, double y1, double y2)
{
return std::sqrt(std::pow(x2 - x1, 2) + std::pow(y2 - y1, 2));
}
double Square::computeArea() const
{
double length = distance(coordX[0], coordX[1], coordY[0], coordY[1]);
double width = distance(coordX[0], coordX[3], coordY[0], coordY[3]);
return length * width;
}
This allows your rectangle to be at any arbitrary orientation, instead of x-y axis aligned. You just need to maintain a convention of the indexes of the corners, like in my example diagram.
Lets say I have two AABB based areas, each area defined by two coordinates mins{x, y} and maxs{x, y}, I want to find the middle connection point between them.
Since my english is not good, I can't explain all with my words,
see the following picture for easier understanding:
http://i.*.com/WokivEe.png
All I need to find is the red point coordinates.
so If we move this into programming question, actual data structures would look like this:
struct Vec2D {
float x, y;
}
struct Rectangle {
Vec2D min;
Vec2D max;
}
Rectangle obj[2]
Anyone got an idea for an algorithm?
Along either the X or Y axis, sort the coordinates of the sides that touch into order. Then average the 2nd and 3rd ones in that list to find their midpoint. I hope this answers the question sufficiently.
Here is a little algorithm that first find which sides of the objects are closest, and then uses the 4 points along the common side to make a list, sorted along the common axis. The average of the 2 middle points of the sorted list are the answer. This will work for both horizontal and vertical sides. I added accessor functions to the data structures so that they can be indexed; e.g., for a Vec2D, coordinate(0) is the x value and coordinate(1) is the y value.
#include <math.h>
#include <iostream>
#include <limits>
struct Vec2D {
float x, y;
float coordinate(int axis)
{
return (axis & 1) ? y : x;
}
};
struct Rectangle {
Vec2D min;
Vec2D max;
Vec2D corner(int j)
{
return (j & 1) ? max : min;
}
// Get the other corner along the given axis
Vec2D along(int j, int ax)
{
Vec2D p = corner(j);
if (0 == ax)
{
p.x = corner(1-j).x;
}
else
{
p.y = corner(1-j).y;
}
return p;
}
};
using namespace std;
inline Vec2D* vp(const void* p)
{
return (Vec2D*) p;
}
static int compare_x(const void*a, const void*b)
{
if (vp(a)->x < vp(b)->x)
{
return -1;
}
else
if (vp(a)->x > vp(b)->x)
{
return 1;
}
return 0;
}
static int compare_y(const void*a, const void*b)
{
if (vp(a)->y < vp(b)->y)
{
return -1;
}
else
if (vp(a)->y > vp(b)->y)
{
return 1;
}
return 0;
}
int main(void) {
int ax; // axis index
int c0, c1;
float gap = numeric_limits<float>::max();
struct Rectangle obj[2] = {0,2,10,10,10,5,15,20};
struct
{
int ax,c0,c1;
} closest;
// Find out which sides are the closest to each other
for(ax = 0; 2 > ax; ++ax) // Look at x axis and y axis
{
for(c0 = 0; 2 > c0; ++c0) // Look at both corners of obj[0]
{
for(c1 = 0; 2 > c1; ++c1) // Look at both corners of obj[1]
{
float dist = fabs(obj[0].corner(c0).coordinate(ax) - obj[1].corner(c1).coordinate(ax));
if (dist < gap)
{
gap = dist;
closest.ax = ax;
closest.c0 = c0;
closest.c1 = c1;
}
}
}
}
int other = 1 - closest.ax; // The other axis
cout << "The closest gap is along the " << (closest.ax ? 'y' : 'x') << " axis\n";
cout << "The common side is along the " << (other ? 'y' : 'x') << " direction\n";
// Make a list of the 4 points along the common side
Vec2D list[4];
list[0] = obj[0].corner(closest.c0);
list[1] = obj[0].along(closest.c0, other);
list[2] = obj[1].corner(closest.c1);
list[3] = obj[1].along(closest.c1, other);
// Sort them into order along the common axis
qsort(list, 4, sizeof(Vec2D), closest.ax ? compare_x : compare_y);
// Get the average of the 2 middle points along the common axis.
Vec2D answer = {
(list[1].x + list[2].x) / 2,
(list[1].y + list[2].y) / 2
};
cout << "(" << answer.x << "," << answer.y << ")\n";
}
bool isOnPerimeter - function that I need help with.
bool isOnPerimeter(int row, int column, int radius)
{
double dRow=static_cast<double>(row);
double dColumn=static_cast<double>(column);
double dRadius=static_cast<double>(radius);
if (pow(dRow,2.0)+pow(dColumn,2.0)<=pow(dRadius,2.0) &&
pow(dRow,2.0)+pow(abs(dColumn)+1,2.0) > pow(dRadius,2.0))
return true;
else
return false;
}
void drawCircle(int radius)
{
for (int row = -radius;row <= radius;++row)
{
for (int column = -radius;column <= radius;++column)
{
if (isOnPerimeter(row,column,radius))
cout << "*";
else
cout << " ";
cout << endl;
}
}
}
the function looks like it's drawing a circle inside the square define by coordinates (-radius,-radius), (radius,radius).
How it does that: consider the trigonometric circle, you know that sin^2 + cos^2 = R^2. Since sin and cos are the projections of R on oy and ox axes, all the points inside the circle have the property that sin^2 + cos^2 < R^2 and all the points outside the circle have the property sin^2 + cos^2 > R^2
In your example you row, col are the equivalent of sin, cos. So you determine the edge of the circle as being all the points for which
sin^2 + cos^2 <= R^2 && sin^2 + (cos+1)^2 > R^2
Hope this helps
I'm using the cv::projectPoints to get correspondent pixels of a vector of 3D points.
The points are all near each other.
The problem is that for some points I get correct pixels coordinates but for other I get strange negative values like -22599...
Is it normal that cv::projectPoints return negative values or is it a bug in my code?
void SingleCameraTriangulator::projectPointsToImage2(const std::vector< cv::Vec3d >& pointsGroup, const double scale, std::vector< Pixel >& pixels)
{
cv::Vec3d
t2, r2;
decomposeTransformation(*g_12_, r2, t2);
cv::Mat imagePoints2;
cv::projectPoints(pointsGroup, r2, t2, *camera_matrix_, *distortion_coefficients_, imagePoints2);
for (std::size_t i = 0; i < imagePoints2.rows; i++)
{
cv::Vec2d pixel = imagePoints2.at<cv::Vec2d>(i);
Pixel p;
p.x_ = pixel[0];
p.y_ = pixel[1];
if ( (p.x_ < 0) || (p.x_ > ((1 / scale) * img_1_->cols)) || (p.y_ < 0) || (p.y_ > ((1/scale) * img_1_->rows)))
{
cv::Vec3d point = pointsGroup[i];
std::cout << point << " - " << pixel << " - " << pixel*scale << "problema" << std::endl;
}
p.i_ = getBilinearInterpPix32f(*img_2_, scale * p.x_, scale * p.y_);
pixels.push_back(p);
}
}
Thank you in advance for any suggestions.
reprojectImageTo3D (are you use it for getting 3D points?) gives large z coordinates (10000) for outlier points, so I think your problem is here.