Related
Let's say AB1, AB2, CD1, CD2. AB1&AB2 and CD1&CD2 3D Points makes a Line Segment. And the Said Line segments are Not in the same Plane.
AP is a point Line segment AB1&AB2,
BP is a point Line segment CD1&CD2.
Point1 and Point2 Closest To each other (Shortest distance between the two line segment)
Now, how can I Find the said two points Point1 and Point2? What method should I use?
Below is only partially solved For full solution please See this answer here... because This function does not work when Two Line is on the same plane...
Thanks to #MBo I have come across Geometry GoldMine of Code and Explanations! They have Many Source Code Contributors! i picked one from there here it is clean and great!
bool CalculateLineLineIntersection(Vector3D p1, Vector3D p2, Vector3D p3, Vector3D p4, Vector3D& resultSegmentPoint1, Vector3D& resultSegmentPoint2)
{
// Algorithm is ported from the C algorithm of
// Paul Bourke at http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline3d/
resultSegmentPoint1 = { 0,0,0 };
resultSegmentPoint2 = { 0,0,0 };
Vector3D p13 = VectorMinus(p1, p3);
Vector3D p43 = VectorMinus(p4, p3);
/*if (p43.LengthSq() < Math.Epsilon) {
return false;
}*/
Vector3D p21 = VectorMinus(p2, p1);
/*if (p21.LengthSq() < Math.Epsilon) {
return false;
}*/
double d1343 = p13.x * (double)p43.x + (double)p13.y * p43.y + (double)p13.z * p43.z;
double d4321 = p43.x * (double)p21.x + (double)p43.y * p21.y + (double)p43.z * p21.z;
double d1321 = p13.x * (double)p21.x + (double)p13.y * p21.y + (double)p13.z * p21.z;
double d4343 = p43.x * (double)p43.x + (double)p43.y * p43.y + (double)p43.z * p43.z;
double d2121 = p21.x * (double)p21.x + (double)p21.y * p21.y + (double)p21.z * p21.z;
double denom = d2121 * d4343 - d4321 * d4321;
/*if (Math.Abs(denom) < Math.Epsilon) {
return false;
}*/
double numer = d1343 * d4321 - d1321 * d4343;
double mua = numer / denom;
double mub = (d1343 + d4321 * (mua)) / d4343;
resultSegmentPoint1.x = (float)(p1.x + mua * p21.x);
resultSegmentPoint1.y = (float)(p1.y + mua * p21.y);
resultSegmentPoint1.z = (float)(p1.z + mua * p21.z);
resultSegmentPoint2.x = (float)(p3.x + mub * p43.x);
resultSegmentPoint2.y = (float)(p3.y + mub * p43.y);
resultSegmentPoint2.z = (float)(p3.z + mub * p43.z);
return true;
}
So Far I have Tried All these Below which works only when both Line segments have the same Magnitude...
Link 1
Link 2
I tried Calculating the centroid of both line segments and calculating the nearest Point on Segment From the midpoint. (I know how to calculate the Closest Point line segment from another Point)
But This only works when Both Line segments are of equal length AND each of Both the Linesegment's MidPoint is perpendicular to Each other and the centroid...
NOTE:Visual Geometry Geogbra3D for a visual representation of these Points
NOTE:AB1CD means From Point AB1 to Line CD(not segment)
AB1 = (6.550000, -7.540000, 0.000000 )
AB2 = (4.540000, -3.870000, 6.000000 )
CD1 = (0.000000, 8.000000, 3.530000 )
CD2 = (0.030000, -7.240000, -1.340000 )
PointCD1AB = (3.117523, -1.272742, 10.246199 )
PointCD2AB = (6.318374, -7.117081, 0.691420 )
PointAB1CD = (0.029794, -7.135321, -1.306549 )
PointAB2CD = (0.019807, -2.062110, 0.314614 )
Magntidue of PointCD1AB - P1LineSegmentCD = 11.866340
Magntidue of PointCD2AB - P2LineSegmentCD = 6.609495
Magntidue of PointAB1CD - P1LineSegmentAB = 6.662127
Magntidue of PointAB2CD - P2LineSegmentAB = 9.186399
Magntidue of PointCD1AB - PointAB1CD = 13.318028
Magntidue of PointCD2AB - PointAB2CD = 8.084965
Magntidue of PointCD1AB - PointAB2CD = 10.433375
Magntidue of PointCD2AB - PointAB1CD = 6.598368
Actual Shortest Point are
Point1 = (0.01, 1.59, 1.48 )
Point2 = (-1.23, 1.11, 3.13 )
Magnitude of Point1 And Point2 = 2.1190799890518526
For the Above Data, I used this Below Function
void NearestPointBetweenTwoLineSegmentOfVariedLength(Vector3D P1LineSegmentAB, Vector3D P2LineSegmentAB, Vector3D P1LineSegmentCD, Vector3D P2LineSegmentCD, Vector3D Testing)
{
/* float Line1Mag = Magnitude(VectorMinus(P1LineSegmentAB, P2LineSegmentAB));
float Line2Mag = Magnitude(VectorMinus(P1LineSegmentCD, P2LineSegmentCD));
P2LineSegmentAB = VectorMinus(P2LineSegmentAB, P1LineSegmentAB);
P1LineSegmentCD = VectorMinus(P1LineSegmentCD, P1LineSegmentAB);
P2LineSegmentCD = VectorMinus(P2LineSegmentCD, P1LineSegmentAB);
P1LineSegmentAB = VectorMinus(P1LineSegmentAB, P1LineSegmentAB);
Vector3D P1P2UnitDirection = GetUnitVector(P2LineSegmentAB, { 0,0,0 });
AngleBetweenTwoVectorsWithCommonUnitVectorAngleOfSecondArgument(P1LineSegmentAB, P2LineSegmentAB, P1P2UnitDirection);*/
Vector3D ReturnVal;
Vector3D PointCD1AB;
Vector3D PointCD2AB;
Vector3D PointAB1CD;
Vector3D PointAB2CD;
NearestPointOnLineFromPoint(P1LineSegmentCD, P1LineSegmentAB, P2LineSegmentAB, PointCD1AB, false);
PrintVector3Dfor(VectorMinus(PointCD1AB, Testing), "PointCD1AB", true);
NearestPointOnLineFromPoint(P2LineSegmentCD, P1LineSegmentAB, P2LineSegmentAB, PointCD2AB, false);
PrintVector3Dfor(VectorMinus(PointCD2AB, Testing), "PointCD2AB", true);
NearestPointOnLineFromPoint(P1LineSegmentAB, P1LineSegmentCD, P2LineSegmentCD, PointAB1CD, false);
PrintVector3Dfor(VectorMinus(PointAB1CD, Testing), "PointAB1CD", true);
NearestPointOnLineFromPoint(P2LineSegmentAB, P1LineSegmentCD, P2LineSegmentCD, PointAB2CD, false);
PrintVector3Dfor(VectorMinus(PointAB2CD, Testing), "PointAB2CD", true);
float m1 = Magnitude(VectorMinus(PointCD1AB, P1LineSegmentCD));
float m2 = Magnitude(VectorMinus(PointCD2AB, P2LineSegmentCD));
float m3 = Magnitude(VectorMinus(PointAB1CD, P1LineSegmentAB));
float m4 = Magnitude(VectorMinus(PointAB1CD, P2LineSegmentAB));
float m5 = Magnitude(VectorMinus(PointCD1AB, PointAB1CD));
float m6 = Magnitude(VectorMinus(PointCD2AB, PointAB2CD));
float m7 = Magnitude(VectorMinus(PointCD1AB, PointAB2CD));
float m8 = Magnitude(VectorMinus(PointCD2AB, PointAB1CD));
Printfloatfor(m1, "Magntidue of PointCD1AB - P1LineSegmentCD");
Printfloatfor(m2, "Magntidue of PointCD2AB - P2LineSegmentCD");
Printfloatfor(m3, "Magntidue of PointAB1CD - P1LineSegmentAB");
Printfloatfor(m4, "Magntidue of PointAB2CD - P2LineSegmentAB");
Printfloatfor(m5, "Magntidue of PointCD1AB - PointAB1CD");
Printfloatfor(m6, "Magntidue of PointCD2AB - PointAB2CD");
Printfloatfor(m7, "Magntidue of PointCD1AB - PointAB2CD");
Printfloatfor(m8, "Magntidue of PointCD2AB - PointAB1CD");
//NearestPointBetweenTwoLineSegmentOfSameLength1(P1LineSegmentAB, P2LineSegmentAB, P1LineSegmentCD, P2LineSegmentCD);
//NearestPointBetweenTwoLineSegmentOfSameLength2(P1LineSegmentAB, P2LineSegmentAB, P1LineSegmentCD, P2LineSegmentCD);
//NearestPointBetweenTwoLineSegmentOfSameLength3(P1LineSegmentAB, P2LineSegmentAB, P1LineSegmentCD, P2LineSegmentCD);
}
void NearestPointOnLineFromPoint(Vector3D Point, Vector3D LineSegmentStart, Vector3D LineSegmentEnd, Vector3D& ReturnVector, bool ClampTheValue)
{
//Get Heading Direction of Capsule from Origin To End
Vector3D CapsuleHeading = VectorMinus(LineSegmentEnd, LineSegmentStart);
float MagnitudeOfLineSegment = Magnitude(CapsuleHeading);
CapsuleHeading = VectorDivide(CapsuleHeading, MagnitudeOfLineSegment);
// Project From Point to Origin
Vector3D Projection = VectorMinus(Point, LineSegmentStart);
float DotProd = DotProduct(Projection, CapsuleHeading);
if (ClampTheValue)
{
DotProd = Clamp(DotProd, 0.0f, MagnitudeOfLineSegment);
}
ReturnVector = VectorAdd(LineSegmentStart, VectorMultiply(CapsuleHeading, DotProd));
}
I have Converted This Code from C# to C++ and it is not working as intended... I don't know if there is a problem with my code conversion or a problem within the code itself?
Vector3D ClampPointToLine(Vector3D pointToClamp, Vector3D LineStart, Vector3D LineEnd)
{
Vector3D clampedPoint = {0,0,0};
double minX, minY, minZ, maxX, maxY, maxZ;
if (LineStart.x <= LineEnd.x)
{
minX = LineStart.x;
maxX = LineEnd.x;
}
else
{
minX = LineEnd.x;
maxX = LineStart.x;
}
if (LineStart.y <= LineEnd.y)
{
minY = LineStart.y;
maxY = LineEnd.y;
}
else
{
minY = LineEnd.y;
maxY = LineStart.y;
}
if (LineStart.z <= LineEnd.z)
{
minZ = LineStart.z;
maxZ = LineEnd.z;
}
else
{
minZ = LineEnd.z;
maxZ = LineStart.z;
}
clampedPoint.x = (pointToClamp.x < minX) ? minX : (pointToClamp.x > maxX) ? maxX : pointToClamp.x;
clampedPoint.y = (pointToClamp.y < minY) ? minY : (pointToClamp.y > maxY) ? maxY : pointToClamp.y;
clampedPoint.z = (pointToClamp.z < minZ) ? minZ : (pointToClamp.z > maxZ) ? maxZ : pointToClamp.z;
return clampedPoint;
}
void distBetweenLines(Vector3D p1, Vector3D p2, Vector3D p3, Vector3D p4, Vector3D& ClosestPointOnLineP1P2, Vector3D& ClosestPointOnLineP3P4)
{
Vector3D d1;
Vector3D d2;
d1 = VectorMinus(p2, p1);
d2 = VectorMinus(p4, p3);
double eq1nCoeff = (d1.x * d2.x) + (d1.y * d2.y) + (d1.z * d2.z);
double eq1mCoeff = (-(powf(d1.x, 2)) - (powf(d1.y, 2)) - (powf(d1.z, 2)));
double eq1Const = ((d1.x * p3.x) - (d1.x * p1.x) + (d1.y * p3.y) - (d1.y * p1.y) + (d1.z * p3.z) - (d1.z * p1.z));
double eq2nCoeff = ((powf(d2.x, 2)) + (powf(d2.y, 2)) + (powf(d2.z, 2)));
double eq2mCoeff = -(d1.x * d2.x) - (d1.y * d2.y) - (d1.z * d2.z);
double eq2Const = ((d2.x * p3.x) - (d2.x * p1.x) + (d2.y * p3.y) - (d2.y * p2.y) + (d2.z * p3.z) - (d2.z * p1.z));
double M[2][3] = { { eq1nCoeff, eq1mCoeff, -eq1Const }, { eq2nCoeff, eq2mCoeff, -eq2Const } };
int rowCount = 2;
// pivoting
for (int col = 0; col + 1 < rowCount; col++) if (M[col, col] == 0)
// check for zero coefficients
{
// find non-zero coefficient
int swapRow = col + 1;
for (; swapRow < rowCount; swapRow++) if (M[swapRow, col] != 0) break;
if (M[swapRow, col] != 0) // found a non-zero coefficient?
{
// yes, then swap it with the above
double tmp[2];
for (int i = 0; i < rowCount + 1; i++)
{
tmp[i] = M[swapRow][i];
M[swapRow][i] = M[col][i];
M[col][i] = tmp[i];
}
}
else
{
std::cout << "\n the matrix has no unique solution";
return; // no, then the matrix has no unique solution
}
}
// elimination
for (int sourceRow = 0; sourceRow + 1 < rowCount; sourceRow++)
{
for (int destRow = sourceRow + 1; destRow < rowCount; destRow++)
{
double df = M[sourceRow][sourceRow];
double sf = M[destRow][sourceRow];
for (int i = 0; i < rowCount + 1; i++)
M[destRow][i] = M[destRow][i] * df - M[sourceRow][i] * sf;
}
}
// back-insertion
for (int row = rowCount - 1; row >= 0; row--)
{
double f = M[row][row];
if (f == 0) return;
for (int i = 0; i < rowCount + 1; i++) M[row][i] /= f;
for (int destRow = 0; destRow < row; destRow++)
{
M[destRow][rowCount] -= M[destRow][row] * M[row][rowCount]; M[destRow][row] = 0;
}
}
double n = M[0][2];
double m = M[1][2];
Vector3D i1 = { p1.x + (m * d1.x), p1.y + (m * d1.y), p1.z + (m * d1.z) };
Vector3D i2 = { p3.x + (n * d2.x), p3.y + (n * d2.y), p3.z + (n * d2.z) };
Vector3D i1Clamped = ClampPointToLine(i1, p1, p2);
Vector3D i2Clamped = ClampPointToLine(i2, p3, p4);
ClosestPointOnLineP1P2 = i1Clamped;
ClosestPointOnLineP3P4 = i2Clamped;
return;
}
Your problem is to find the shortest connection P1P2 between two line segments AB and CD. Let us define l1 as the line which goes through the points A and B and l2 as the line which goes through C and D.
You can split this problem up into several subtasks:
finding the shortest connection between the lines l1 and l2.
finding the shortest connection from either of the points A, B to segment CD (likewise for C,D to segment AB).
Let's start with the first subtask. THe line l1, going through A and B, can be parametrised by a single scalar, say sc,
l1(sc) = u*sc + A
with the direction vector u=(B-A).
As a consequence, we also have l1(0) = A and l(1) = B. Now, we want to find the minimal distance between this line and another line going through C and D, i.e.
l2(c) = v*tc + C
with v = D-C. In analogy to the other line, we have have l2(0) = C and l(1) = D. Now, we define
f(sc, tc) = 1/2*|l1(sc)-l2(tc)|^2
which is nothing but half the distance between the two lines squared. If we now want to minimise this function, we need to satisfy
df/dsc = 0 and df/dtc = 0
You'll find that
df/dsc = [u*sc - v*tc + (A-C)]*u and df/dtc = [u*sc - v*tc + (A-C)]*(-v)
Introducing w=A-C and arranging in vectors and matrices yields:
[ u*u -v*u] * [sc] = -[ w*u]
[-u*v v*v] [tc] [-w*v]
m * result = -rhs
The solution of the linear system is result = -m^(⁻1)* rhs, where m^(⁻1) is the inverse of m. If a and c are less than 0 or greater than 1, the closest point of the lines is outside the segments AB and CD. You might return these values as well.
The second subtask is closely related to this problem, but we minimise
f(sc) = 1/2*|l1(sc)-P|^2 and g(tc) = 1/2*|l2(tc)-P|^2
which directly yields
sc = -(A-P)*u/(u*u) and rc = -(C-P)*v/(v*v)
If sc < 0 we set sc = 0 or if sc > 1 we set sc = 1 (and likewise for tc) in order to get points on the segments.
Here is the implementation, which I took from here and modified it.
First, we define some helpers, i.e. vectors and some basic mathematical relations.
template <int dim>
struct Vector
{
std::array<double, dim> components;
};
using Vector2D = Vector<2>;
using Vector3D = Vector<3>;
// subtract
template <int dim>
Vector<dim> operator-(const Vector<dim> &u, const Vector<dim> &v) {
Vector<dim> result(u);
for (int i = 0; i < dim; ++i)
result.components[i] -= v.components[i];
return result;
}
// add
template <int dim>
Vector<dim> operator+(const Vector<dim> &u, const Vector<dim> &v) {
Vector<dim> result(u);
for (int i = 0; i < dim; ++i)
result.components[i] += v.components[i];
return result;
}
// negate
template <int dim>
Vector<dim> operator-(const Vector<dim> &u) {
Vector<dim> result;
for (int i = 0; i < dim; ++i)
result.components[i] = -u.components[i];
return result;
}
// scalar product
template <int dim>
double operator*(const Vector<dim> &u, const Vector<dim> &v) {
double result = 0;
for (int i = 0; i < dim; ++i)
result += u.components[i] * v.components[i];
return result;
}
// scale
template <int dim>
Vector<dim> operator*(const Vector<dim> &u, const double s) {
Vector<dim> result(u);
for (int i = 0; i < dim; ++i)
result.components[i] *= s;
return result;
}
// scale
template <int dim>
Vector<dim> operator*(const double s, const Vector<dim> &u) {
return u*s;
}
// ostream
template <int dim>
std::ostream& operator<< (std::ostream& out, const Vector<dim> &u) {
out << "(";
for (auto c : u.components)
out << std::setw(15) << c ;
out << ")";
return out;
}
This function does the actual work:
std::pair<Vector3D, Vector3D>
shortest_connection_segment_to_segment(const Vector3D A, const Vector3D B,
const Vector3D C, const Vector3D D)
{
Vector3D u = B - A;
Vector3D v = D - C;
Vector3D w = A - C;
double a = u*u; // always >= 0
double b = u*v;
double c = v*v; // always >= 0
double d = u*w;
double e = v*w;
double sc, sN, sD = a*c - b*b; // sc = sN / sD, sD >= 0
double tc, tN, tD = a*c - b*b; // tc = tN / tD, tD >= 0
double tol = 1e-15;
// compute the line parameters of the two closest points
if (sD < tol) { // the lines are almost parallel
sN = 0.0; // force using point A on segment AB
sD = 1.0; // to prevent possible division by 0.0 later
tN = e;
tD = c;
}
else { // get the closest points on the infinite lines
sN = (b*e - c*d);
tN = (a*e - b*d);
if (sN < 0.0) { // sc < 0 => the s=0 edge is visible
sN = 0.0; // compute shortest connection of A to segment CD
tN = e;
tD = c;
}
else if (sN > sD) { // sc > 1 => the s=1 edge is visible
sN = sD; // compute shortest connection of B to segment CD
tN = e + b;
tD = c;
}
}
if (tN < 0.0) { // tc < 0 => the t=0 edge is visible
tN = 0.0;
// recompute sc for this edge
if (-d < 0.0) // compute shortest connection of C to segment AB
sN = 0.0;
else if (-d > a)
sN = sD;
else {
sN = -d;
sD = a;
}
}
else if (tN > tD) { // tc > 1 => the t=1 edge is visible
tN = tD;
// recompute sc for this edge
if ((-d + b) < 0.0) // compute shortest connection of D to segment AB
sN = 0;
else if ((-d + b) > a)
sN = sD;
else {
sN = (-d + b);
sD = a;
}
}
// finally do the division to get sc and tc
sc = (fabs(sN) < tol ? 0.0 : sN / sD);
tc = (fabs(tN) < tol ? 0.0 : tN / tD);
Vector3D P1 = A + (sc * u);
Vector3D P2 = C + (tc * v);
return {P1, P2}; // return the closest distance
}
Usage:
int main() {
Vector3D A = {-7.54, 6.55, 0 };
Vector3D B = {4.54, -3.87, 6.0 };
Vector3D C = {0.0, 8.0, 3.53 };
Vector3D D = {0.03, -7.24, -1.34 };
auto [P1, P2] = shortest_connection_segment_to_segment (A, B, C, D);
std::cout << "P1 = " << P1 << std::endl;
std::cout << "P2 = " << P2 << std::endl;
return 0;
}
This prints
P1 = ( -1.24635 1.1212 3.12599)
P2 = ( 0.0125125 1.64365 1.49881)
live demo.
Note that this code still requires more testing.
Below Is a "Compact" version of the code from #StefanKssmr which is Here, This "Compact" version can easily be ported to OpenCL
Many thanks to #StefanKssmr for posting the Correct Answer,
void NearestPointBetweenTwoLineSegment(Vector3D AB1, Vector3D AB2, Vector3D CD1, Vector3D CD2, Vector3D& resultSegmentPoint1, Vector3D& resultSegmentPoint2)
{
Vector3D u = VectorMinus(AB2, AB1);
Vector3D v = VectorMinus(CD2, CD1);
Vector3D w = VectorMinus(AB1, CD1);
double a = DotProduct(u, u); // always >= 0
double b = DotProduct(u, v);
double c = DotProduct(v, v); // always >= 0
double d = DotProduct(u, w);
double e = DotProduct(v, w);
double sN, sD = (a * c) - (b * b); // sc = sN / sD, default sD = D >= 0
double tN, tD = (a * c) - (b * b); // tc = tN / tD, default tD = D >= 0
float Temp1;
float Temp2;
float Temp3;// Unfortuantely i have no choice but to use this...
//Part 1
Temp1 = (sD < 1e-6f) ? 1.0f : 0.0f;
sN = (1.0f - Temp1) * (b * e - c * d);
sD = ((1.0f - Temp1) * sD) + Temp1;
tN = (Temp1 * e) + ((1.0f - Temp1) * ((a * e) - (b * d)));
tD = (Temp1 * c) + ((1.0f - Temp1) * tD);
Temp2 = (sN < 0.0f) ? 1.0f : 0.0f;
Temp2 = Temp2 * (1.0f - Temp1);
sN = ((1.0f - Temp2) * sN);
tN = ((1.0f - Temp2) * tN) + (Temp2 * e);
tD = ((1.0f - Temp2) * tD) + (Temp2 * c);
Temp2 = ((sN > sD) ? 1.0f : 0.0f) * (1.0f - Temp2);
Temp2 = Temp2 * (1.0f - Temp1);
sN = ((1.0f - Temp2) * sN) + (Temp2 * sD);
tN = ((1.0f - Temp2) * tN) + (Temp2 * (e + b));
tD = ((1.0f - Temp2) * tD) + (Temp2 * c);
//Part 2.1
Temp1 = (tN < 0.0f) ? 1.0f : 0.0f;
tN = tN * (1.0f - Temp1);
Temp2 = (((-d) < 0.0) ? 1.0f : 0.0f) * Temp1;
sN = (1.0f - Temp2) * sN;//sN = (Temp2 * 0) + ((1.0f - Temp2) * sN);
Temp3 = ((((-d) > a) ? 1.0f : 0.0f) * (1.0f - Temp2)) * (Temp1);
sN = (Temp3 * sD) + ((1.0f - Temp3) * (sN));
Temp2 = (1.0f - Temp3) * (1.0f - Temp2) * (Temp1);
sN = (Temp2 * (-d)) + ((1.0f - Temp2) * (sN));
sD = (Temp2 * a) + ((1.0f - Temp2) * (sD));
//Part 2.2
Temp1 = ((tN > tD) ? 1.0f : 0.0f) * (1.0f - Temp1);
tN = ((1.0f - Temp1) * tN) + (Temp1 * tD);
Temp2 = (((-d + b) < 0.0) ? 1.0f : 0.0f) * Temp1;
sN = (1.0f - Temp2) * sN;//sN = (Temp2 * 0) + ((1.0f - Temp2) * sN);
Temp3 = ((((-d + b) > a) ? 1.0f : 0.0f) * (1.0f - Temp2)) * (Temp1);
sN = (Temp3 * sD) + ((1.0f - Temp3) * (sN));
Temp2 = (1.0f - Temp3) * (1.0f - Temp2) * (Temp1);
sN = (Temp2 * (-d)) + ((1.0f - Temp2) * (sN));
sD = (Temp2 * a) + ((1.0f - Temp2) * (sD));
resultSegmentPoint1 = VectorAdd(AB1, VectorMultiply(u, (fabs(sN) < 1e-6f ? 0.0 : sN / sD)));
resultSegmentPoint2 = VectorAdd(CD1, VectorMultiply(v, (fabs(tN) < 1e-6f ? 0.0 : tN / tD)));
}
I'm using this function to convert a normalized value between 0 and 1 to an RGB value depending on the JET colormap.
std::vector<double> mapJet(double v, double vmin, double vmax)
{
if (v < vmin)
v = vmin;
if (v > vmax)
v = vmax;
double dr, dg, db;
if (v < 0.1242) {
db = 0.504 + ((1.-0.504) / 0.1242)*v;
dg = dr = 0.;
} else if (v < 0.3747) {
db = 1.;
dr = 0.;
dg = (v - 0.1242) * (1. / (0.3747-0.1242));
} else if (v < 0.6253) {
db = (0.6253 - v) * (1. / (0.6253-0.3747));
dg = 1.;
dr = (v - 0.3747) * (1. / (0.6253-0.3747));
} else if (v < 0.8758) {
db = 0.;
dr = 1.;
dg = (0.8758 - v) * (1. / (0.8758-0.6253));
} else {
db = 0.;
dg = 0.;
dr = 1. - (v - 0.8758) * ((1.-0.504) / (1.-0.8758));
}
return std::vector<double> { 255 * dr, 255 * dg, 255 * db };
}
My aim is to find the function double v = mapJet_inv(R,G,B). That is to say, I convert an RGB color to a normalized value between 0 and 1 depending on the colormap. I tried to start from the end of the mapJet function, however I didn't know how to specify the ranges of the R G B components.
Maybe I'm doing this badly. I will appreciate your help.
Thank you
vector<float> colors_to_value(vector<float> colors_tab)
{
double v ,db,dg,dr; v=db=dg=dr=0; vector<float> values_result;
for(int i=0;i<colors_tab.size();i++)
{
dr=colors_tab(i,0)/255.;
dg=colors_tab(i,1)/255.;
db=colors_tab(i,2)/255.;
if ( dg == 0. && dr == 0. ) {
v = (db - 0.504) / (1.-0.504) * 0.1242; // a revoir
}
else if ( db == 1. && dr == 0. ) {
v = dg/4. + 0.1242;
}
else if (db==0 && dr==1)
{
v = 0.8758 -dg*(1. / (0.8758-0.6253)) ;
}
else if ( db==0 && dg ==0)
{
v =( 1. - dr ) / ((1.-0.504) / (1.-0.8758)) + 0.8758;
}
else{
float val1= 0.6253 - db/(1. / (0.6253-0.3747)) ;
float val2=dr/ (1. / (0.6253-0.3747)) + 0.3747;
// v=(val1+val2)/2.;
if (val1>val2)
v=val1;
}
if(v<0) v=0;
values_result.push_back(v);
}
return values_result;
}
My slerp routine is below. From what I've read, the check against > 0 should handle it so it always takes the shortest path. But it never does. In the case where I cross a "pole", the quaternion flips out and produces angles with NAN values.
quat quat::slerp(quat dest, float t)
{
const quat &from = *this;
static const double epsilon = 0.0001;
double theta, cosTheta, sinTheta;
double p, q;
cosTheta = from.x*dest.x + from.y*dest.y + from.z*dest.z + from.w*dest.w;
if(cosTheta < 0.0)
{
dest = { -from.x, -from.y, -from.z, -from.w };
cosTheta = -cosTheta;
}
if((1.0-fabs(cosTheta)) > epsilon)
{
theta = acos(cosTheta);
sinTheta = sin(theta);
q = sin((1-t) * theta) / sinTheta;
p = sin(t*theta) / sinTheta;
}
else
{
q = 1-t;
p = t;
}
quat qo;
qo.w = (float)((q * from.w) + (p * dest.w));
qo.x = (float)((q * from.x) + (p * dest.x));
qo.y = (float)((q * from.y) + (p * dest.y));
qo.z = (float)((q * from.z) + (p * dest.z));
return qo;
}
Maybe there are other bugs as well, but this line certainly has one:
dest = { -from.x, -from.y, -from.z, -from.w };
It overwrites dest with -from, which is not correct. It should be:
dest = { -dest.x, -dest.y, -dest.z, -dest.w };
Could anyone help me about B-spline Curve error?
I want to draw B-spline Curve in c++, but even though all coordinates are positive, the segment's coordinate is negative.
This is B-spline Curve code.
void BSplineCurve(Dot &ControlPoint1, Dot &ControlPoint2,
Dot &ControlPoint3,Dot &ControlPoint4,
Dot &DrawCurve, double &t){
double t2 = t * t;
double t3 = t2 * t;
double mt3 = (1 - t) * (1 - t) * (1 - t);
double bi3 = mt3 / 6;
double bi2 = ((3 * t3) - (6 * t2) + 4) / 6;
double bi1 = ((-3 * t3) + (3 * t2) + (3 * t) + 1) / 6;
double bi = mt3 / 6;
DrawCurve.x = ControlPoint1.x * bi3;
DrawCurve.x += ControlPoint2.x * bi2;
DrawCurve.x += ControlPoint3.x * bi1;
DrawCurve.x += ControlPoint4.x * bi;
DrawCurve.y = ControlPoint1.y * bi3;
DrawCurve.y += ControlPoint2.y * bi2;
DrawCurve.y += ControlPoint3.y * bi1;
DrawCurve.y += ControlPoint4.y * bi;
}
This is Drawing Code.
double t = 3.f;
do{
if ((3 < t) && (t <= 4)) {
BSplineCurve(ControlPoint1, ControlPoint2, ControlPoint3, ControlPoint4, DrawCurve, t);
Draw1Dot(DrawCurve.x, DrawCurve.y, DrawCurve.R, DrawCurve.G, DrawCurve.B);
}
else if ((4 < t) && (t <= 5)) {
BSplineCurve(ControlPoint2, ControlPoint3, ControlPoint4, ControlPoint5, DrawCurve, t);
Draw1Dot(DrawCurve.x, DrawCurve.y, DrawCurve.R, DrawCurve.G, DrawCurve.B);
}
else if ((5 < t) && (t <= 6)) {
BSplineCurve(ControlPoint3, ControlPoint4, ControlPoint5, ControlPoint6, DrawCurve, t);
Draw1Dot(DrawCurve.x, DrawCurve.y, DrawCurve.R, DrawCurve.G, DrawCurve.B);
}
t += 0.001;
} while(t < 6.001);
This is Control Point's coordinate.
Poiont1 : 50, 50
Poiont2 : 50, 100
Poiont3 : 200, 100
Poiont4 : 200, 50
Poiont5 : 350, 50
Poiont6 : 350, 100
But this is 1st segment's coordinate.
Q3 : -1543, -349
Your drawing code looks wrong.
In function BSplineCurve the t parameter should take values in [0, 1] range. By changing t from 0 to 1 one will build a cubic B-spline between points ControlPoint2 and ControlPoint3.
You could try something like:
Dot points[6] = {ControlPoint1, ControlPoint2, ControlPoint3, ControlPoint4, ControlPoint5, ControlPoint6};
for(double t = 3.0; t < 6.0; t += 0.001)
{
const int start = static_cast<int>(t);
BSplineCurve(points[start - 3],
points[start - 2],
points[start - 1],
points[start],
DrawCurve,
t - start);
Draw1Dot(DrawCurve.x, DrawCurve.y, DrawCurve.R, DrawCurve.G, DrawCurve.B);
}
Update
Your B-spline calculation code looks wrong too :-)
bi should be t3/6.0 and not mt3/6.0. See here (slide 25).
The fixed function can look something like this (I did not test it):
void BSplineCurve(const Dot &point1,
const Dot &point2,
const Dot &point3,
const Dot &point4,
const double t,
Dot &result)
{
const double t2 = t * t;
const double t3 = t2 * t;
const double mt = 1.0 - t;
const double mt3 = mt * mt * mt;
const double bi3 = mt3;
const double bi2 = 3 * t3 - 6 * t2 + 4;
const double bi1 =-3 * t3 + 3 * t2 + 3 * t + 1;
const double bi = t3;
result.x = point1.x * bi3 +
point2.x * bi2 +
point3.x * bi1 +
point4.x * bi;
result.x /= 6.0;
result.y = point1.y * bi3 +
point2.y * bi2 +
point3.y * bi1 +
point4.y * bi;
result.y /= 6.0;
}
Maybe the point you use is too very close. In spline it is not good iidea to use very close point. becaue so we have very "galloping" curve. Like this:
red is original
When I add some extra formal parameters double tmin=0.0, double tmax=0.0 to the constructor of the Ray in the code below, I always obtain a wrong image with a white top border. These formal parameters currently contribute in no way (i.e. are unused) to the code. So how is it possible to obtain a different image?
System specifications:
OS: Windows 8.1
Compiler: MSVC 2015
Code:
#include "stdafx.h"
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <random>
std::default_random_engine generator(606418532);
std::uniform_real_distribution<double> distribution = std::uniform_real_distribution<double>(0.0, 1.0);
double erand48(unsigned short *x) {
return distribution(generator);
}
#define M_PI 3.14159265358979323846
struct Vector3 {
double x, y, z;
Vector3(double x_ = 0, double y_ = 0, double z_ = 0) { x = x_; y = y_; z = z_; }
Vector3 operator+(const Vector3 &b) const { return Vector3(x + b.x, y + b.y, z + b.z); }
Vector3 operator-(const Vector3 &b) const { return Vector3(x - b.x, y - b.y, z - b.z); }
Vector3 operator*(double b) const { return Vector3(x*b, y*b, z*b); }
Vector3 mult(const Vector3 &b) const { return Vector3(x*b.x, y*b.y, z*b.z); }
Vector3& norm() { return *this = *this * (1 / sqrt(x*x + y*y + z*z)); }
double Dot(const Vector3 &b) const { return x*b.x + y*b.y + z*b.z; } // cross:
Vector3 operator%(Vector3&b) { return Vector3(y*b.z - z*b.y, z*b.x - x*b.z, x*b.y - y*b.x); }
};
//struct Ray { Vector3 o, d; Ray(const Vector3 &o_, const Vector3 &d_, double tmin=0.0, double tmax=0.0) : o(o_), d(d_) {} };
struct Ray { Vector3 o, d; Ray(const Vector3 &o_, const Vector3 &d_) : o(o_), d(d_) {} };
enum Reflection_t { DIFFUSE, SPECULAR, REFRACTIVE };
struct Sphere {
double rad; // radius
Vector3 p, e, f; // position, emission, color
Reflection_t reflection_t; // reflection type (DIFFuse, SPECular, REFRactive)
Sphere(double rad_, Vector3 p_, Vector3 e_, Vector3 f_, Reflection_t reflection_t) :
rad(rad_), p(p_), e(e_), f(f_), reflection_t(reflection_t) {}
double intersect(const Ray &r) const {
Vector3 op = p - r.o;
double t, eps = 1e-4, b = op.Dot(r.d), det = b*b - op.Dot(op) + rad*rad;
if (det<0) return 0; else det = sqrt(det);
return (t = b - det)>eps ? t : ((t = b + det)>eps ? t : 0);
}
};
Sphere spheres[] = {
Sphere(1e5, Vector3(1e5 + 1,40.8,81.6), Vector3(),Vector3(.75,.25,.25),DIFFUSE),//Left
Sphere(1e5, Vector3(-1e5 + 99,40.8,81.6),Vector3(),Vector3(.25,.25,.75),DIFFUSE),//Rght
Sphere(1e5, Vector3(50,40.8, 1e5), Vector3(),Vector3(.75,.75,.75),DIFFUSE),//Back
Sphere(1e5, Vector3(50,40.8,-1e5 + 170), Vector3(),Vector3(), DIFFUSE),//Frnt
Sphere(1e5, Vector3(50, 1e5, 81.6), Vector3(),Vector3(.75,.75,.75),DIFFUSE),//Botm
Sphere(1e5, Vector3(50,-1e5 + 81.6,81.6),Vector3(),Vector3(.75,.75,.75),DIFFUSE),//Top
Sphere(16.5,Vector3(27,16.5,47), Vector3(),Vector3(1,1,1)*.999, SPECULAR),//Mirr
Sphere(16.5,Vector3(73,16.5,78), Vector3(),Vector3(1,1,1)*.999, REFRACTIVE),//Glas
Sphere(600, Vector3(50,681.6 - .27,81.6),Vector3(12,12,12), Vector3(), DIFFUSE) //Lite
};
inline double clamp(double x) { return x<0 ? 0 : x>1 ? 1 : x; }
inline int toInt(double x) { return int(pow(clamp(x), 1 / 2.2) * 255 + .5); }
inline bool intersect(const Ray &r, double &t, int &id) {
double n = sizeof(spheres) / sizeof(Sphere), d, inf = t = 1e20;
for (int i = int(n); i--;) if ((d = spheres[i].intersect(r)) && d<t) { t = d; id = i; }
return t<inf;
}
Vector3 radiance(const Ray &r_, int depth_, unsigned short *Xi) {
double t; // distance to intersection
int id = 0; // id of intersected object
Ray r = r_;
int depth = depth_;
Vector3 cl(0, 0, 0); // accumulated color
Vector3 cf(1, 1, 1); // accumulated reflectance
while (1) {
if (!intersect(r, t, id)) return cl; // if miss, return black
const Sphere &obj = spheres[id]; // the hit object
Vector3 x = r.o + r.d*t, n = (x - obj.p).norm(), nl = n.Dot(r.d)<0 ? n : n*-1, f = obj.f;
double p = f.x>f.y && f.x>f.z ? f.x : f.y>f.z ? f.y : f.z; // max refl
cl = cl + cf.mult(obj.e);
if (++depth>5) if (erand48(Xi)<p) f = f*(1 / p); else return cl; //R.R.
cf = cf.mult(f);
if (obj.reflection_t == DIFFUSE) { // Ideal DIFFUSE reflection
double r1 = 2 * M_PI*erand48(Xi), r2 = erand48(Xi), r2s = sqrt(r2);
Vector3 w = nl, u = ((fabs(w.x)>.1 ? Vector3(0, 1) : Vector3(1)) % w).norm(), v = w%u;
Vector3 d = (u*cos(r1)*r2s + v*sin(r1)*r2s + w*sqrt(1 - r2)).norm();
r = Ray(x, d);
continue;
}
else if (obj.reflection_t == SPECULAR) {
r = Ray(x, r.d - n * 2 * n.Dot(r.d));
continue;
}
Ray reflRay(x, r.d - n * 2 * n.Dot(r.d));
bool into = n.Dot(nl)>0;
double nc = 1, nt = 1.5, nnt = into ? nc / nt : nt / nc, ddn = r.d.Dot(nl), cos2t;
if ((cos2t = 1 - nnt*nnt*(1 - ddn*ddn))<0) {
r = reflRay;
continue;
}
Vector3 tdir = (r.d*nnt - n*((into ? 1 : -1)*(ddn*nnt + sqrt(cos2t)))).norm();
double a = nt - nc, b = nt + nc, R0 = a*a / (b*b), c = 1 - (into ? -ddn : tdir.Dot(n));
double Re = R0 + (1 - R0)*c*c*c*c*c, Tr = 1 - Re, P = .25 + .5*Re, RP = Re / P, TP = Tr / (1 - P);
if (erand48(Xi)<P) {
cf = cf*RP;
r = reflRay;
}
else {
cf = cf*TP;
r = Ray(x, tdir);
}
continue;
}
}
int main(int argc, char *argv[]) {
int w = 512, h = 384, samps = argc == 2 ? atoi(argv[1]) / 4 : 1; // # samples
Ray cam(Vector3(50, 52, 295.6), Vector3(0, -0.042612, -1).norm()); // cam pos, dir
Vector3 cx = Vector3(w*.5135 / h), cy = (cx%cam.d).norm()*.5135, r, *c = new Vector3[w*h];
#pragma omp parallel for schedule(dynamic, 1) private(r) // OpenMP
for (int y = 0; y<h; y++) { // Loop over image rows
fprintf(stderr, "\rRendering (%d spp) %5.2f%%", samps * 4, 100.*y / (h - 1));
for (unsigned short x = 0, Xi[3] = { 0,0,y*y*y }; x<w; x++) // Loop cols
for (int sy = 0, i = (h - y - 1)*w + x; sy<2; sy++) // 2x2 subpixel rows
for (int sx = 0; sx<2; sx++, r = Vector3()) { // 2x2 subpixel cols
for (int s = 0; s<samps; s++) {
double r1 = 2 * erand48(Xi), dx = r1<1 ? sqrt(r1) - 1 : 1 - sqrt(2 - r1);
double r2 = 2 * erand48(Xi), dy = r2<1 ? sqrt(r2) - 1 : 1 - sqrt(2 - r2);
Vector3 d = cx*(((sx + .5 + dx) / 2 + x) / w - .5) +
cy*(((sy + .5 + dy) / 2 + y) / h - .5) + cam.d;
r = r + radiance(Ray(cam.o + d * 140, d.norm()), 0, Xi)*(1. / samps);
} // Camera rays are pushed ^^^^^ forward to start in interior
c[i] = c[i] + Vector3(clamp(r.x), clamp(r.y), clamp(r.z))*.25;
}
}
FILE *fp;
fopen_s(&fp, "image.ppm", "w"); // Write image to PPM file.
fprintf(fp, "P3\n%d %d\n%d\n", w, h, 255);
for (int i = 0; i<w*h; i++)
fprintf(fp, "%d %d %d ", toInt(c[i].x), toInt(c[i].y), toInt(c[i].z));
}
First Ray structure:
struct Ray { Vector3 o, d; Ray(const Vector3 &o_, const Vector3 &d_) : o(o_), d(d_) {} };
Results in:
Second Ray structure:
struct Ray { Vector3 o, d; Ray(const Vector3 &o_, const Vector3 &d_, double tmin=0.0, double tmax=0.0) : o(o_), d(d_) {} };
Results in:
The last image has a noticeable white top border which is not present in the first image.
Edit:
I used
size_t n = sizeof(spheres) / sizeof(Sphere);
Now I obtain the same images, but I also checked if the original int(n) could differ from 9 which is never the case.
Ok this is from the Debug build, which is different from the Release build.
Sounds like a memory error, looking quickly at your code I'm sceptical of this line:
for (int i = int(n); i--;) if ((d = spheres[i].intersect(r)) && d<t)
I suspect accessing sphere[i] is out of bounds, perhaps you should try sphere[i-1]. You could also try compiling your code with a compiler that adds extra code for debugging/sanitising/checking memory addresses.