I have checked out this question, but the answer is very large for me:
How to know if a line intersects a plane in C#? - Basic 2D geometry
Is there any .NET method to know if a line defined by two points intersects a rectangle?
public bool Intersects(Point a, Point b, Rectangle r)
{
// return true if the line intersects the rectangle
// false otherwise
}
Thanks in advance.
public static bool LineIntersectsRect(Point p1, Point p2, Rectangle r)
{
return LineIntersectsLine(p1, p2, new Point(r.X, r.Y), new Point(r.X + r.Width, r.Y)) ||
LineIntersectsLine(p1, p2, new Point(r.X + r.Width, r.Y), new Point(r.X + r.Width, r.Y + r.Height)) ||
LineIntersectsLine(p1, p2, new Point(r.X + r.Width, r.Y + r.Height), new Point(r.X, r.Y + r.Height)) ||
LineIntersectsLine(p1, p2, new Point(r.X, r.Y + r.Height), new Point(r.X, r.Y)) ||
(r.Contains(p1) && r.Contains(p2));
}
private static bool LineIntersectsLine(Point l1p1, Point l1p2, Point l2p1, Point l2p2)
{
float q = (l1p1.Y - l2p1.Y) * (l2p2.X - l2p1.X) - (l1p1.X - l2p1.X) * (l2p2.Y - l2p1.Y);
float d = (l1p2.X - l1p1.X) * (l2p2.Y - l2p1.Y) - (l1p2.Y - l1p1.Y) * (l2p2.X - l2p1.X);
if( d == 0 )
{
return false;
}
float r = q / d;
q = (l1p1.Y - l2p1.Y) * (l1p2.X - l1p1.X) - (l1p1.X - l2p1.X) * (l1p2.Y - l1p1.Y);
float s = q / d;
if( r < 0 || r > 1 || s < 0 || s > 1 )
{
return false;
}
return true;
}
Unfortunately the wrong answer has been voted up. It is much to expensive to compute the actual intersection points, you only need comparisons. The keyword to look for is "Line Clipping" (http://en.wikipedia.org/wiki/Line_clipping). Wikipedia recommends the Cohen-Sutherland algorithm (http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland) when you want fast rejects, which is probably the most common scenario. There is a C++-implementation on the wikipedia page. If you are not interested in actually clipping the line, you can skip most of it.
The answer of #Johann looks very similar to that algorithm, but I didn't look at it in detail.
Brute force algorithm...
First check if the rect is to the left or right of the line endpoints:
Establish the leftmost and rightmost X values of the line endpoints: XMIN and XMAX
If Rect.Left > XMAX, then no intersection.
If Rect.Right < XMIN, then no intersection.
Then, if the above wasn't enough to rule out intersection, check if the rect is above or below the line endpoints:
Establish the topmost and bottommost Y values of the line endpoints: YMAX and YMIN
If Rect.Bottom > YMAX, then no intersection.
If Rect.Top < YMIN, then no intersection.
Then, if the above wasn't enough to rule out intersection, you need to check the equation of the line, y = m * x + b, to see if the rect is above the line:
Establish the line's Y-value at Rect.Left and Rect.Right: LINEYRECTLEFT and LINEYRECTRIGHT
If Rect.Bottom > LINEYRECTRIGHT && Rect.Bottom > LINEYRECTLEFT, then no intersection.
Then, if the above wasn't enough to rule out intersection, you need to check if the rect is below the line:
If Rect.Top < LINEYRECTRIGHT && Rect.Top < LINEYRECTLEFT, then no intersection.
Then, if you get here:
Intersection.
N.B. I'm sure there's a more elegant algebraic solution, but performing these steps geometrically with pen and paper is easy to follow.
Some untested and uncompiled code to go with that:
public struct Line
{
public int XMin { get { ... } }
public int XMax { get { ... } }
public int YMin { get { ... } }
public int YMax { get { ... } }
public Line(Point a, Point b) { ... }
public float CalculateYForX(int x) { ... }
}
public bool Intersects(Point a, Point b, Rectangle r)
{
var line = new Line(a, b);
if (r.Left > line.XMax || r.Right < line.XMin)
{
return false;
}
if (r.Top < line.YMin || r.Bottom > line.YMax)
{
return false;
}
var yAtRectLeft = line.CalculateYForX(r.Left);
var yAtRectRight = line.CalculateYForX(r.Right);
if (r.Bottom > yAtRectLeft && r.Bottom > yAtRectRight)
{
return false;
}
if (r.Top < yAtRectLeft && r.Top < yAtRectRight)
{
return false;
}
return true;
}
This code has better performance:
public static bool SegmentIntersectRectangle(
double rectangleMinX,
double rectangleMinY,
double rectangleMaxX,
double rectangleMaxY,
double p1X,
double p1Y,
double p2X,
double p2Y)
{
// Find min and max X for the segment
double minX = p1X;
double maxX = p2X;
if (p1X > p2X)
{
minX = p2X;
maxX = p1X;
}
// Find the intersection of the segment's and rectangle's x-projections
if (maxX > rectangleMaxX)
{
maxX = rectangleMaxX;
}
if (minX < rectangleMinX)
{
minX = rectangleMinX;
}
if (minX > maxX) // If their projections do not intersect return false
{
return false;
}
// Find corresponding min and max Y for min and max X we found before
double minY = p1Y;
double maxY = p2Y;
double dx = p2X - p1X;
if (Math.Abs(dx) > 0.0000001)
{
double a = (p2Y - p1Y)/dx;
double b = p1Y - a*p1X;
minY = a*minX + b;
maxY = a*maxX + b;
}
if (minY > maxY)
{
double tmp = maxY;
maxY = minY;
minY = tmp;
}
// Find the intersection of the segment's and rectangle's y-projections
if (maxY > rectangleMaxY)
{
maxY = rectangleMaxY;
}
if (minY < rectangleMinY)
{
minY = rectangleMinY;
}
if (minY > maxY) // If Y-projections do not intersect return false
{
return false;
}
return true;
}
You can also check how it's work in JS demo: http://jsfiddle.net/77eej/2/
If you have two Points and Rect you can call this function like that:
public static bool LineIntersectsRect(Point p1, Point p2, Rect r)
{
return SegmentIntersectRectangle(r.X, r.Y, r.X + r.Width, r.Y + r.Height, p1.X, p1.Y, p2.X, p2.Y);
}
I took HABJAN's solution, which worked well, and converted it to Objective-C. The Objective-C code is as follows:
bool LineIntersectsLine(CGPoint l1p1, CGPoint l1p2, CGPoint l2p1, CGPoint l2p2)
{
CGFloat q = (l1p1.y - l2p1.y) * (l2p2.x - l2p1.x) - (l1p1.x - l2p1.x) * (l2p2.y - l2p1.y);
CGFloat d = (l1p2.x - l1p1.x) * (l2p2.y - l2p1.y) - (l1p2.y - l1p1.y) * (l2p2.x - l2p1.x);
if( d == 0 )
{
return false;
}
float r = q / d;
q = (l1p1.y - l2p1.y) * (l1p2.x - l1p1.x) - (l1p1.x - l2p1.x) * (l1p2.y - l1p1.y);
float s = q / d;
if( r < 0 || r > 1 || s < 0 || s > 1 )
{
return false;
}
return true;
}
bool LineIntersectsRect(CGPoint p1, CGPoint p2, CGRect r)
{
return LineIntersectsLine(p1, p2, CGPointMake(r.origin.x, r.origin.y), CGPointMake(r.origin.x + r.size.width, r.origin.y)) ||
LineIntersectsLine(p1, p2, CGPointMake(r.origin.x + r.size.width, r.origin.y), CGPointMake(r.origin.x + r.size.width, r.origin.y + r.size.height)) ||
LineIntersectsLine(p1, p2, CGPointMake(r.origin.x + r.size.width, r.origin.y + r.size.height), CGPointMake(r.origin.x, r.origin.y + r.size.height)) ||
LineIntersectsLine(p1, p2, CGPointMake(r.origin.x, r.origin.y + r.size.height), CGPointMake(r.origin.x, r.origin.y)) ||
(CGRectContainsPoint(r, p1) && CGRectContainsPoint(r, p2));
}
Many thanks HABJAN. I will note that at first I wrote my own routine which checked each point along the gradient, and I did everything I could do to maximize performance, but this was immediately far faster.
For Unity (inverts y!). This takes care of division by zero problem that other approaches here have:
using System;
using UnityEngine;
namespace Util {
public static class Math2D {
public static bool Intersects(Vector2 a, Vector2 b, Rect r) {
var minX = Math.Min(a.x, b.x);
var maxX = Math.Max(a.x, b.x);
var minY = Math.Min(a.y, b.y);
var maxY = Math.Max(a.y, b.y);
if (r.xMin > maxX || r.xMax < minX) {
return false;
}
if (r.yMin > maxY || r.yMax < minY) {
return false;
}
if (r.xMin < minX && maxX < r.xMax) {
return true;
}
if (r.yMin < minY && maxY < r.yMax) {
return true;
}
Func<float, float> yForX = x => a.y - (x - a.x) * ((a.y - b.y) / (b.x - a.x));
var yAtRectLeft = yForX(r.xMin);
var yAtRectRight = yForX(r.xMax);
if (r.yMax < yAtRectLeft && r.yMax < yAtRectRight) {
return false;
}
if (r.yMin > yAtRectLeft && r.yMin > yAtRectRight) {
return false;
}
return true;
}
}
}
The simplest computational geometry technique is to just walk through the segments of the polygon and see if it intersects with any of them, as it then must also intersect the polygon.
The only caveat of this method (and most of CG) is that we have to be careful about edge cases. What if the line crosses the rectangle at a point - do we count that as intersection or not? Be careful in your implementation.
Edit: The typical tool for the line-intersects-segment calculation is a LeftOf(Ray, Point) test, which returns if the point is the to the left of the ray. Given a line l (which we use as a ray) and a segment containing points a and b, the line intersects the segment if one point is to the left and one point is not:
(LeftOf(l,a) && !LeftOf(l,b)) || (LeftOf(l,b) && !LeftOf(l,a))
Again, you need to watch out for edge-cases, when the point is on the line, but depends how you wish to actually define intersection.
There is no simple predefined .NET method you can call to accomplish that. However, using the Win32 API, there is a pretty easy way to do this (easy in the sense of implementation, performance is not the strong point): LineDDA
BOOL LineDDA(int nXStart,int nYStart,int nXEnd,int nYEnd,LINEDDAPROC lpLineFunc,LPARAM lpData)
This functions calls the callback function for every pixel of the line to be drawn. In this function, you can check if the pixel is within your rectangle - if you find one, then it intersects.
As I said, this is not the fastest solution, but pretty easy to implement. To use it in C#, you will of course need to DllImport it from gdi32.dll.
[DllImport("gdi32.dll")] public static extern int LineDDA(int n1,int n2,int n3,int n4,int lpLineDDAProc,int lParam);
Related
I am creating an utility to display graphics in the console. I currently have a method that takes 2 coordinates and draws a line between those two points. Is it possible to create a method based on this that will take 3 coordinates and draw a filled triangle?
I was thinking of drawing 2 lines, and then drawing lines from each point of one line to each point of the other. However, I think that the time complexity of this method will be really bad.
Here is a method that I used to draw a line:
void drawLine(Line line)
{
const bool steep = (fabs(line.end.y - line.begin.y) > fabs(line.end.x - line.begin.x));
if (steep)
{
std::swap(line.begin.x, line.begin.y);
std::swap(line.end.x, line.end.y);
}
if (line.begin.x > line.end.x)
{
std::swap(line.begin, line.end);
}
const double dx = line.end.x - line.begin.x;
const double dy = fabs(line.end.y - line.begin.y);
const double zStepLength = fabs(fabs(line.end.z) - fabs(line.begin.z)) / dx;
double error = dx / 2.0;
const int ystep = (line.begin.y < line.end.y) ? 1 : -1;
const double zstep = (line.begin.z < line.end.z) ? zStepLength : -zStepLength;
for (double x = line.begin.x, z = line.begin.z; x <= line.end.x; x++, z += zstep)
{
if (steep)
{
setPixel({ line.begin.y, x, z }, line.color);
}
else
{
setPixel({ x, line.begin.y, z }, line.color);
}
error -= dy;
if (error < 0)
{
line.begin.y += ystep;
error += dx;
}
}
}
I can use the function CheckCollisionCircleRec(Vector2{ x, y }, radius, paddleRect) to find out simply if my circle has collided with my rectangle, but I want to be able to find out what side of the rectangle my circle has collided with. How would I go about doing this? None of the algorithms I've made are working. Example of my most recent blunder:
if (x - radius <= 0 || x + radius >= screenWidth) {
speedX *= -1;
}
else if (y - radius <= 0 || y + radius >= screenHeight) {
speedY *= -1;
}
else if (CheckCollisionCircleRec(Vector2{ x, y }, radius, paddleRect)) {
float paddleBottom = paddleRect.y + paddleRect.height;
float paddleRight = paddleRect.x + paddleRect.width;
if (range(paddleRect.x, paddleRect.x + speedX / 100, x + radius)) {
x = paddleRect.x - radius;
speedX *= -1;
}
if (range(paddleRight - speedX / 100, paddleRight, x - radius)) {
x = paddleRight + radius;
speedX *= -1;
};
if (range(paddleRect.y, paddleRect.y + speedY / 100, y + radius)) {
y = paddleRect.y - radius;
speedY *= -1;
}
if (range(paddleBottom - speedY / 100, paddleBottom, y - radius)) {
y = paddleBottom + radius;
speedY *= -1;
};
EDIT:
Here's the function I used to get the working end result:
// px and py are the ball's previous locations
// x and y are the ball's current locations
void checkCollision(Rectangle rectangle) {
int left = rectangle.x;
int right = rectangle.x + rectangle.width;
int top = rectangle.y;
int bottom = rectangle.y + rectangle.height;
if (CheckCollisionCircleRec(Vector2{ x, y }, radius, rectangle)) {
if (px < left) {
speedX = negative(speedX);
x = left - radius;
}
else if (px > right) {
speedX = positive(speedX);
x = right + radius;
}
else if (py < top) {
speedY = negative(speedY);
y = top - radius;
}
else if (py > bottom) {
speedY = positive(speedY);
y = bottom + radius;
};
};
};
A simply way is to use the PREVIOUS location of your circle. Not sure if you can in your program, but since you have an x and y handy, I'll assume you can have a prevX and prevY. I'll also assume these values represent the CENTER of the circle.
Now if (prevX < paddleRect.x), then you likely collided with the left side (not guaranteed, but resolving ambiguities with complete accuracy requires recursively simulating your physics at smaller and smaller timesteps, which is likely unnecessary here). You can also constrain this more tightly with something like if (prevX < paddleRect.x && prevY > paddleRect.y && prevY < paddleRect.y + paddRect.height). There are various constraints you can add depending on how cleanly you want the side to be hit before detecting it. You can add corner hits, etc.
The reason for using the previous location is that, if your circle is moving fast enough, then in a single frame it can jump straight into the middle of the rectangle. It's usually necessary to use the previous position to give more specific collision information in the current-location collision
My task is to detect an orange ball in video. I detected by thresholding image on HSV colorspace and bounding box. Then I have center and radius of ball, with unit is pixel.
When ball is static, I expect center and radius will be static too, but reality, it has noise. I use Kalman Filter to filter noise and it works well. But it delay in real-time. I try to optimize covariance parameters but not work.
So could anyone help me static center and radius when ball is static and without delay?
Are you sure it is the Kalman Filter witch is causing the delay.
Otherwise you can try this lazy filter witch only is noise rejecting but blazingly fast. My suspicion however it is the HSV conversion.
class noiseFilter
{
private:
cv::Point2f ptLast;
float ptMaxTol;
public:
noiseFilter(float maxTol = 1.5f)
{
ptMaxTol = maxTol * maxTol; // we do the pow(2) here so we don't have to do a square root on every update
ptLast = cv::Point2f(0.0f, 0.0f);
}
cv::Point2f update(cv::Point2f &ptNew) // update filter with new found point
{
float dist = pDistance2(ptLast, ptNew);
if (dist > ptMaxTol) ptLast = ptNew; // update only if distance is more than threshold
return ptLast;
}
cv::Point2f getResult() // get result of filter
{
return ptLast;
}
private:
// calculate distance between 2 point without doing a sqrt
float pDistance2(cv::Point2f &p1, cv::Point2f &p2)
{
float dx = p1.x - p2.x;
float dy = p1.y - p2.y;
return (dx * dx + dy * dy);
}
};
int main()
{
cv::Point2f pt;
noiseFilter filter(2.1f); // initialize filter wit max 2.1 pixels noise rejection.
int x = 100, y = 120;
for (int i = 0; i < 100; i++)
{
// generate some noise with 2 pixels variation
pt.x = ((rand() % 200) - 100) * 0.01f + x;
pt.y = ((rand() % 200) - 100) * 0.01f + y;
cv::Point2f pts = filter.update(pt);
printf("input x=%6.2f y=%6.2f output x=%6.2f y=%6.2f\r\n", pt.x, pt.y, pts.x, pts.y);
// do som random big update on random intervals
if ((rand() % 50) == 1) {
x += 15;
printf("big update on X\r\n");
}
if ((rand() % 50) == 1){
y += 25;
printf("big update on Y\r\n");
}
}
return 0;
}
Below a noise filter with smoothing.
Works on slow and fast moving objects.
class noiseFilterSmooth
{
private:
static const int maxHist = 10;
cv::Point2f ptLast;
float ptMaxTol;
cv::Point2f hist[maxHist];
int histHead,histSize;
public:
noiseFilterSmooth(float maxTol = 1.5f)
{
histHead = histSize = 0;
ptMaxTol = maxTol * maxTol; // we do the pow(2) here so we don't have to do a square root on every update
ptLast = cv::Point2f(0.0f, 0.0f);
}
cv::Point2f& update(cv::Point2f &ptNew) // update filter with new found point
{
float dist = pDistance2(ptLast, ptNew);
if (dist > ptMaxTol) histSize = histHead = 0; // reset smoothing filter if distance is more than threshold
// update smoothing filter with last result
hist[histHead] = ptNew; // update smoothing filter with last
histHead = (histHead + 1) % maxHist;
if (histSize < maxHist) histSize++;
return getResult();
}
cv::Point2f& getResult() // get result of filter
{
float sumx = 0, sumy = 0;
for (int i = 0; i < histSize; i++)
{
sumx += hist[i].x;
sumy += hist[i].y;
}
ptLast.x = sumx / histSize;
ptLast.y = sumy / histSize;
return ptLast;
}
private:
// calculate distance between 2 point without doing a sqrt
float pDistance2(cv::Point2f &p1, cv::Point2f &p2)
{
float dx = p1.x - p2.x;
float dy = p1.y - p2.y;
return (dx * dx + dy * dy);
}
};
I am trying to figure out whether a set of points is visible to another point. I have a geometry mesh and an observation point(red dot on image). The mesh is broken up into line segments, each with a center (blue arrows). I need to determine if the observation point can see the center of the line segments without any of the other line segments being in between.
The method I have been following is trying to calculate a line perpendicular to the line between the observation point and each of the centers. Then projecting each of the line segments onto this line and checking if the center I am concerned with is in between the start and end point of a projected line segment. I either get everything is visible or invisible to the observation point.
I am using this as reference
My question has two parts. Firstly, I cant figure out why this isn't working and secondly is there a better way to do this?
bool projectPointOntoLine(point observation, vector<segment> segments, int currentIndex){
point currentPoint = segments[currentIndex].getCenter();
double dx = observation.getX() - currentPoint.getX();
double dy = observation.getY() - currentPoint.getY();
double m = -dy/dx;
double mm = m*m;
double newX = calculateNewX(currentPoint.getX(), currentPoint.getY(), m, mm);
double newY = calculateNewY(currentPoint.getX(), currentPoint.getY(), m, mm);
long double minDist = sqrt((currentPoint.getX() - observation.getX())*(currentPoint.getX() - observation.getX()) + (currentPoint.getY() - observation.getY())*(currentPoint.getY() - observation.getY()));
bool visible = true;
for(unsigned ii = 0; ii < segments.size(); ii++){
if(ii != currentIndex){
point mid = segments[ii].getStart();
long double dist = sqrt((mid.getX() - observation.getX())*(mid.getX() - observation.getX()) + (mid.getY() - observation.getY())*(mid.getY() - observation.getY()));
if(dist < minDist) {
point start = segments[ii].getStart();
point end = segments[ii].getEnd();
double newStartX = calculateNewX(start.getX(), start.getY(), m, mm);
double newStartY = calculateNewY(start.getX(), start.getY(), m, mm);
double newEndX = calculateNewX(end.getX(), end.getY(), m, mm);
double newEndY = calculateNewY(end.getX(), end.getY(), m, mm);
if ((newX >= newStartX && newEndX >= newX)){// && (newY >= newStartY && newEndY >= newY)) {
visible = false;
}
else if ((newX >= newEndX && newStartX >= newX)){//} && (newY >= newEndY && newStartY >= newY)) {
visible = false;
}
}
}
}
return visible;
}
double calculateNewX(double x, double y, double gradient, double gradientSquared){
return (1/(1 + gradientSquared))*(x) + (gradient/(1 + gradientSquared))*(y);
}
double calculateNewY(double x, double y, double gradient, double gradientSquared){
return (gradient/(1 + gradientSquared))*(x) + (gradientSquared/(1 + gradientSquared))*(y);
}
This gives me the following result:
Which is only half of the shadowing.
Apparently the problem is here:
if((newX > newStartX && newEndX > newX) && (newY > newStartY && newEndY > newY)){
visible = false;
}
You presume that newStartX is always less than newEndX (same with Y). This is not necessarily the case. Depending on mutual orientation of the segment and the line to project on, the endpoints may flip.
I am trying to build a game in Opengl. Before I start making better movement mechanics I want to get collision working. I have cube-cube collision working and I have sphere-sphere collision working, but can't figure out cube-sphere collision. Since I want it in 3d I have the pivot at the center of the objects. Anyone have any suggestions?
EDIT: This is the code I currently have:
bool SphereRectCollision( Sphere& sphere, Rectangle& rect)
{
//Closest point on collision box
float cX, cY;
//Find closest x offset
if( sphere.getCenterX() < rect.GetCenterX())//checks if the center of the circle is to the left of the rectangle
cX = rect.GetCenterX();
else if( sphere.getCenterX() > rect.GetCenterX() + rect.GetWidth()) //checks if the center of the circle is to the right of the rectangle
cX = rect.GetCenterX() + rect.GetWidth();
else //the circle is inside the rectagle
cX = sphere.getCenterX();
//Find closest y offset
if( sphere.getCenterY() > rect.GetCenterY() + rect.GetHeight() )
cY = rect.GetCenterY();
else if( sphere.getCenterY() < rect.GetCenterY() - rect.GetHeight() )
cY = rect.GetCenterY() + rect.GetHeight();
else
cY = sphere.getCenterY();
//If the closest point is inside the circle
if( distanceSquared( sphere.getCenterX(), sphere.getCenterY(), cX, cY ) < sphere.getRadius() * sphere.getRadius() )
{
//This box and the circle have collided
return false;
}
//If the shapes have not collided
return true;
}
float distanceSquared( float x1, float y1, float x2, float y2 )
{
float deltaX = x2 - x1;
float deltaY = y2 - y1;
return deltaX*deltaX + deltaY*deltaY;
}
I found the solution. I had the right idea, but didn't quite know how to execute it:
bool SphereRectCollision( Sphere& sphere, Rectangle& rect)
{
float sphereXDistance = abs(sphere.X - rect.X);
float sphereYDistance = abs(sphere.Y - rect.Y);
float sphereZDistance = abs(sphere.Z - rect.Z);
if (sphereXDistance >= (rect.Width + sphere.Radius)) { return false; }
if (sphereYDistance >= (rect.Height + sphere.Radius)) { return false; }
if (sphereZDistance >= (rect.Depth + sphere.Radius)) { return false; }
if (sphereXDistance < (rect.Width)) { return true; }
if (sphereYDistance < (rect.Height)) { return true; }
if (sphereZDistance < (rect.GetDepth)) { return true; }
float cornerDistance_sq = ((sphereXDistance - rect.Width) * (sphereXDistance - rect.Width)) +
((sphereYDistance - rect.Height) * (sphereYDistance - rect.Height) +
((sphereYDistance - rect.Depth) * (sphereYDistance - rect.Depth)));
return (cornerDistance_sq < (sphere.Radius * sphere.Radius));
}
This algorithm doesn't work when a hit happen on an edge, the 2nd set of if conditions triggers but a collision isn't occuring