How to Handle Corners in Segment Intersection Checks - c++

I am creating a ray-casting simulation that uses ray-segment intersection checks. On the corners where two segments meet the code determines there is no intersection.
I have tried extending the segments a small distance but this causes other issues with the simulation. What can I do in this situation?
The code for the intersection check:
struct Point {
double x, y;
}
std::unique_ptr<Point> Ray::cast(const Boundary& wall) const {
const double x1 = wall.a.x;
const double y1 = wall.a.y;
const double x2 = wall.b.x;
const double y2 = wall.b.y;
const double x3 = pos.x;
const double y3 = pos.y;
const double x4 = pos.x + dir.x;
const double y4 = pos.y + dir.y;
const double den = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
if (den == 0) {
return nullptr;
}
const double t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / den;
const double u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / den;
if ((t > 0.0f && t < 1.0f) && u > 0.0f) {
return std::make_unique<Point>(x1 + t * (x2 - x1), y1 + t * (y2 - y1));
}
else {
return nullptr;
}
}
Intersection Failure:

You need to include equal to check for when the float is zero:
if ((t >= 0.0f && t <= 1.0f) && u >= 0.0f) {

One standard answer is to define the question in terms of points’ relationship to the ray being cast. It is known how to partition the plane into non-overlapping regions that are “left of”, “right of”, and optionally “on” a line. (The last is optional because you can arbitrarily include it in one of the other two.) Then a ray hits a line segment if the two endpoints are in different regions.

Related

Check parallel lines in line-line collsion

There are two lines defined by the coordinates of P1(x1, x2), P2(x2, x2) and so on.
How can you check if the two lines are parallel? On paper you could evaluate the value of den, when that is 0, the lines are parallel, but how can I do that in floating point arithmetics?
bool Tema1::lineLine(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4)
{
float den = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
float numA = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3);
float numB = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3);
float uA = numA / den;
float uB = numB / den;
return uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1;
}
If I try to compare a short line with one that's long, the den will still be pretty big.
Compare den with sum of squared length of both segments
float den = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
float cmp = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) +
(x4 - x3) * (x4 - x3) + (y4 - y3) * (y4 - y3);
if (abs(den)/cmp < 1e-10) //or another eps value
parallel

C++ Get Point on 3D Vector with Shortest Distance to given Point

I have given a 3D Line which is represented with two 3D Vectors (start-, endpoint), all in C++
vec3 x1 = [x,y,z]
vec3 x2 = [x,y,z]
also i have a 3D Point
vec3 x0 = [x,y,z]
I want to find the Point p which has the shortest distance d to my Point x0 from my given line.
Here's an image as an example:
Thanks for your help!
Take the equation of your line
p = x1 + t (x2 - x1)
The closest point p is such that the vector x0 - p is perpendicular to the line. (You can prove this with Pythagoras / elementary calculus).
Therefore you need
(p - x0) . (x2 - x1) = 0
where . is the dot product.
(x1 - x0 + t (x2 - x1)) . (x2 - x1) = 0
t = - [ (x1 - x0) . (x2 - x1) ] / |x2 - x1|^2
where |x2 - x1| is the magnitude.
Plug this into the first equation to find the p you want.
(PS sorry I couldn't format the equations)
You could project the vector x1->x0 onto the vector x1->x2, and then your point p will be x1 + projected vector. Something like this:
if (x1 == x2)
return x1;
vector x1x0 = x0 - x1;
vector x1x2 = x2 - x1;
float t = x1x0.dot(x1x2) / x1x2.SquaredLength();
if (t <= 0.0)
return x1;
else if(t >= 1.0)
return x2;
else
return x1 + t * x1x2;

nFinding Xcrossing point using Data Points

I was going through Algorithm which identify crossing point using data points on x axis. I can understand calculation but could not understand purpose of this calculation. As per my understanding, it determine each time new y point and slope and subtract them.
I want to insert Image also but I do not have 10 reputation point.
Please let me know if I need to provide info.
//This function determine the crossing point when the graph is intersecting x axis.
XPoint findXingPoint(Curve & curve, double rfix, double vcc, int type, int pull_up)
{
//curve class contain x and y data point
// rfix is fixed value which is generally 50
// vcc also fix value
// type contain 0 and 1
//pull up to identify graph
XPoint p = { 0.0, 0.0 };
double r_fix = rfix;
double m = -1 / r_fix;
double c = vcc / r_fix;
if(type)
c=0;
double r, s, X1, X2, Y1, Y2, Y3, Y4;
r = (m * curve[0].first) + c;
// r is a kind of y value which determine from x and y point
s = abs(r);
for (Curve::iterator i = curve.begin(); i != curve.end(); i++) {
curve_point p = (*i);
double xcurve = p.first;
double ycurve = p.second;
double yloadline = m * xcurve + c;
double B = ycurve - yloadline;
if (B >= 0 && r >= B) {
r = B;
X1 = xcurve;
Y1 = yloadline;
Y2 = ycurve;
}
if (B <= 0 && r >= abs(B)) {
s = abs(B);
X2 = xcurve;
Y4 = yloadline;
Y3 = ycurve;
}
}
#could not understand purpose of B calculation
if (s == 0)
X1 = X2;
if (r == 0)
X2 = X1;
if (X1 != X2) {
double m1, m2, c1, c2;
m1 = (Y3 - Y2) / (X2 - X1);
m2 = (Y4 - Y1) / (X2 - X1);
c1 = Y3 - (m1 * X2);
c2 = Y4 - (m2 * X2);
// CASE m1==m2 should be handled.
p.x = (c2 - c1) / (m1 - m2);
p.y = (m2 * p.x) + c2;
} else {
p.x = X1;
p.y = Y1;
}
#not able to also understand calculation
if (verbosityValue >= 1)
loginfo<<"found crossing point # " << p.x << " " << p.y << endl;
return p;
}
Output:
first
found crossing point # 7.84541e-08 -1.96135e-09 with type 0
found crossing point # 0.528564 0.0182859 with type 1
second
found crossing point # 0.654357 -0.0163589 with type 0
found crossing point # 1.25827 4.31937e-05 with type 1
This appears to be a straightforward implementation. For a given point x,y on the curve, find the slope, draw a line through the slope, find where that line would cross the x axis, and then find the new y value at that point. For well-behaved functions, the new y value is a lot closer to 0 than the initial y value. You iterate until the approximation is good enough.
If you look at the graph, you see that the middle part is quite straight. Hence, the slope of the line is a locally good approximation of the curve, and your new y value is a lot closer to zero (At least 10x times, probably 100x, looking at the graph.0. If you start on the the steeper slopes on either side, you'll need one extra step. Your second x point will be on the middle part.

can the CImg library draw thick lines

I have been using the CImg library, and have been pleased with how easy it is to integrate and use. However, I now want to draw thick lines (i.e., more than one pixel thick). It is not clear from the API documentation of the draw_line function (here) how this can be done. A second version of the function (just below the first in the documentation) even takes a texture as input, but again no width. It seems strange that such a comprehensive library would not have this feature. Perhaps it's supposed to be done using some kind of transformation? I know I could do it using a polygon (i.e., a rectangle where I would compute the corners of the polygon using a normal to the line), but I fear that would be significantly slower.
Apparently, it is not possible 'out-of-the-box', but creating your own routine that calls multiple times the 'draw_line()' routine of CImg, with one or two pixels shifts should give you the result you want, without much work.
This function can be used to draw thick lines as polygons.
void draw_line(cimg_library::CImg<uint8_t>& image,
const int x1, const int y1,
const int x2, const int y2,
const uint8_t* const color,
const unsigned int line_width)
{
if (x1 == x2 && y1 == y2) {
return;
}
// Convert line (p1, p2) to polygon (pa, pb, pc, pd)
const double x_diff = std::abs(x1 - x2);
const double y_diff = std::abs(y1 - y2);
const double w_diff = line_width / 2.0;
// Triangle between pa and p1: x_adj^2 + y_adj^2 = w_diff^2
// Triangle between p1 and p2: x_diff^2 + y_diff^2 = length^2
// Similar triangles: y_adj / x_diff = x_adj / y_diff = w_diff / length
// -> y_adj / x_diff = w_diff / sqrt(x_diff^2 + y_diff^2)
const int x_adj = y_diff * w_diff / std::sqrt(std::pow(x_diff, 2) + std::pow(y_diff, 2));
const int y_adj = x_diff * w_diff / std::sqrt(std::pow(x_diff, 2) + std::pow(y_diff, 2));
// Points are listed in clockwise order, starting from top-left
cimg_library::CImg<int> points(4, 2);
points(0, 0) = x1 - x_adj;
points(0, 1) = y1 + y_adj;
points(1, 0) = x1 + x_adj;
points(1, 1) = y1 - y_adj;
points(2, 0) = x2 + x_adj;
points(2, 1) = y2 - y_adj;
points(3, 0) = x2 - x_adj;
points(3, 1) = y2 + y_adj;
image.draw_polygon(points, color);
}
Benchmarks with line_width 20 and 3 colors. First time is using this function, second time is drawing single 1 px wide line using image.draw_line().
1000,1000 to 2000,2000: 216 µs / 123 µs
2000,2000 to 8000,4000: 588 µs / 151 µs
3000,1000 to 3020,1000: 21 µs / 5 µs
Basically this code does the same as #vll's answer, but also handles the case when (x1-x2)/(y1-y2) < 0 (I remove the abs function).
void draw_line(cimg_library::CImg<uint8_t>& image,
const int x1, const int y1,
const int x2, const int y2,
const uint8_t* const color,
const uint8_t line_width,
const double opacity=1.0)
{
if (x1 == x2 && y1 == y2) {
return;
}
// Convert line (p1, p2) to polygon (pa, pb, pc, pd)
const double x_diff = (x1 - x2);
const double y_diff = (y1 - y2);
const double w_diff = line_width / 2.0;
// Triangle between pa and p1: x_adj^2 + y_adj^2 = w_diff^2
// Triangle between p1 and p2: x_diff^2 + y_diff^2 = length^2
// Similar triangles: y_adj / x_diff = x_adj / y_diff = w_diff / length
// -> y_adj / x_diff = w_diff / sqrt(x_diff^2 + y_diff^2)
const int x_adj = y_diff * w_diff / std::sqrt(std::pow(x_diff, 2) + std::pow(y_diff, 2));
const int y_adj = x_diff * w_diff / std::sqrt(std::pow(x_diff, 2) + std::pow(y_diff, 2));
// Points are listed in clockwise order, starting from top-left
cimg_library::CImg<int> points(4, 2);
points(0, 0) = x1 - x_adj;
points(0, 1) = y1 + y_adj;
points(1, 0) = x1 + x_adj;
points(1, 1) = y1 - y_adj;
points(2, 0) = x2 + x_adj;
points(2, 1) = y2 - y_adj;
points(3, 0) = x2 - x_adj;
points(3, 1) = y2 + y_adj;
image.draw_polygon(points, color, opacity);
}

Given a start and end point, and a distance, calculate a point along a line

Looking for the quickest way to calculate a point that lies on a line
a given distance away from the end point of the line:
void calculate_line_point(int x1, int y1, int x2, int y2, int distance, int *px, int *py)
{
//calculate a point on the line x1-y1 to x2-y2 that is distance from x2-y2
*px = ???
*py = ???
}
Thanks for the responses, no this is not homework, just some hacking out of
my normal area of expertise.
This is the function suggested below. It's not close to working. If I
calculate points every 5 degrees on the upper right 90 degree portion of
a circle as starting points and call the function below with the center of the circle as x2,y2 with a distance of 4 the end points are totally wrong. They lie below and to the right of the center and the length is as long as the center point. Anyone have any suggestions?
void calculate_line_point(int x1, int y1, int x2, int y2, int distance)
{
//calculate a point on the line x1-y1 to x2-y2 that is distance from x2-y2
double vx = x2 - x1; // x vector
double vy = y2 - y1; // y vector
double mag = sqrt(vx*vx + vy*vy); // length
vx /= mag;
vy /= mag;
// calculate the new vector, which is x2y2 + vxvy * (mag + distance).
px = (int) ( (double) x2 + vx * (mag + (double)distance) );
py = (int) ( (double) y2 + vy * (mag + (double)distance) );
}
I've found this solution on stackoverflow but don't understand it completely, can anyone clarify?
I think this belongs on MathOverflow, but I'll answer since this is your first post.
First you calculate the vector from x1y1 to x2y2:
float vx = x2 - x1;
float vy = y2 - y1;
Then calculate the length:
float mag = sqrt(vx*vx + vy*vy);
Normalize the vector to unit length:
vx /= mag;
vy /= mag;
Finally calculate the new vector, which is x2y2 + vxvy * (mag + distance).
*px = (int)((float)x1 + vx * (mag + distance));
*py = (int)((float)y1 + vy * (mag + distance));
You can omit some of the calculations multiplying with distance / mag instead.
These equations are wrong:
px = (int) ( (double) x2 + vx * (mag + (double)distance) );
py = (int) ( (double) y2 + vy * (mag + (double)distance) );
The correct equations are:
px = (int) ( (double) x2 + vx * (double)distance );
py = (int) ( (double) y2 + vy * (double)distance );
Tom