I need to do a boolean subtraction between two models in C#. One of the meshs will be entirely within the other mesh, so I was hoping to reverse the normals for the one model and add the two models together. I am at a loss on how to invert the normals though.
This is how I'm calculating a surface normal:
//creates surface normals
Vector3D CalculateSurfaceNormal(Point3D p1, Point3D p2, Point3D p3)
{
Vector3D v1 = new Vector3D(0, 0, 0); // Vector 1 (x,y,z) & Vector 2 (x,y,z)
Vector3D v2 = new Vector3D(0, 0, 0);
Vector3D normal = new Vector3D(0, 0, 0);
// Finds The Vector Between 2 Points By Subtracting
// The x,y,z Coordinates From One Point To Another.
// Calculate The Vector From Point 2 To Point 1
v1.X = p1.X - p2.X;
v1.Y = p1.Y - p2.Y;
v1.Z = p1.Z - p2.Z;
// Calculate The Vector From Point 3 To Point 2
v2.X = p2.X - p3.X;
v2.Y = p2.Y - p3.Y;
v2.Z = p2.Z - p3.Z;
// Compute The Cross Product To Give Us A Surface Normal
normal.X = v1.Y * v2.Z - v1.Z * v2.Y; // Cross Product For Y - Z
normal.Y = v1.Z * v2.X - v1.X * v2.Z; // Cross Product For X - Z
normal.Z = v1.X * v2.Y - v1.Y * v2.X; // Cross Product For X - Y
normal.Normalize();
return normal;
}
I was advised to reverse the normal by negating it:
n = CalculateSurfaceNormal(p1, p2, p3);
n = new Vector3D(-1 * n.X, -1 * n.Y, -1 * n.Z);
I find the values are negated, but when I view the model in a 3D program, there is no change in the model.
Another suggestion was to try backface culling by changing the order of the vectors. I tried this by swapping the order of v1 and v2:
//creates invertedsurface normals
Vector3D CalculateInvertedSurfaceNormal(Point3D p1, Point3D p2, Point3D p3)
{
Vector3D v1 = new Vector3D(0, 0, 0); // Vector 1 (x,y,z) & Vector 2 (x,y,z)
Vector3D v2 = new Vector3D(0, 0, 0);
Vector3D normal = new Vector3D(0, 0, 0);
// Finds The Vector Between 2 Points By Subtracting
// The x,y,z Coordinates From One Point To Another.
// Calculate The Vector From Point 2 To Point 1
v2.X = p1.X - p2.X;
v2.Y = p1.Y - p2.Y;
v2.Z = p1.Z - p2.Z;
// Calculate The Vector From Point 3 To Point 2
v1.X = p2.X - p3.X;
v1.Y = p2.Y - p3.Y;
v1.Z = p2.Z - p3.Z;
// Compute The Cross Product To Give Us A Surface Normal
normal.X = v1.Y * v2.Z - v1.Z * v2.Y; // Cross Product For Y - Z
normal.Y = v1.Z * v2.X - v1.X * v2.Z; // Cross Product For X - Z
normal.Z = v1.X * v2.Y - v1.Y * v2.X; // Cross Product For X - Y
normal.Normalize();
return normal;
}
No change in the model.
Here is the whole code:
private void SaveMoldMeshtoStlFile(MeshGeometry3D mesh, string filename)
{
if (mesh == null)
return;
if (File.Exists(filename))
{
File.SetAttributes(filename, FileAttributes.Normal);
File.Delete(filename);
}
Point3DCollection vertexes = mesh.Positions;
Int32Collection indexes = mesh.TriangleIndices;
Point3D p1, p2, p3;
Vector3D n;
string text;
using (TextWriter writer = new StreamWriter(filename))
{
writer.WriteLine("solid Bolus");
for (int v = 0; v < mesh.TriangleIndices.Count(); v += 3)
{
//gather the 3 points for the face and the normal
p1 = vertexes[indexes[v]];
p2 = vertexes[indexes[v + 1]];
p3 = vertexes[indexes[v + 2]];
n = CalculateInvertedSurfaceNormal(p1, p2, p3);
text = string.Format("facet normal {0} {1} {2}", n.X,n.Y, n.Z);
writer.WriteLine(text);
writer.WriteLine("outer loop");
text = String.Format("vertex {0} {1} {2}", p1.X, p1.Y, p1.Z);
writer.WriteLine(text);
text = String.Format("vertex {0} {1} {2}", p2.X, p2.Y, p2.Z);
writer.WriteLine(text);
text = String.Format("vertex {0} {1} {2}", p3.X, p3.Y, p3.Z);
writer.WriteLine(text);
writer.WriteLine("endloop");
writer.WriteLine("endfacet");
}
}
}
//creates inverted surface normals
Vector3D CalculateInvertedSurfaceNormal(Point3D p1, Point3D p2, Point3D p3)
{
Vector3D v1 = new Vector3D(0, 0, 0); // Vector 1 (x,y,z) & Vector 2 (x,y,z)
Vector3D v2 = new Vector3D(0, 0, 0);
Vector3D normal = new Vector3D(0, 0, 0);
// Finds The Vector Between 2 Points By Subtracting
// The x,y,z Coordinates From One Point To Another.
// Calculate The Vector From Point 2 To Point 1
v2.X = p1.X - p2.X;
v2.Y = p1.Y - p2.Y;
v2.Z = p1.Z - p2.Z;
// Calculate The Vector From Point 3 To Point 2
v1.X = p2.X - p3.X;
v1.Y = p2.Y - p3.Y;
v1.Z = p2.Z - p3.Z;
// Compute The Cross Product To Give Us A Surface Normal
normal.X = v1.Y * v2.Z - v1.Z * v2.Y; // Cross Product For Y - Z
normal.Y = v1.Z * v2.X - v1.X * v2.Z; // Cross Product For X - Z
normal.Z = v1.X * v2.Y - v1.Y * v2.X; // Cross Product For X - Y
normal.Normalize();
return normal;
}
Is there an error with my code? Am I missing something? I've tried out the exported models in a few different programs, and all are showing the exported model still has the normals facing outwards. I tried flipping the normals in Blender and found the other programs also showed the normals flipped, so I'm fairly sure it's a problem with my program.
Figured out the solution.
The order of points for each triangle is crucial. If the order of points doesn't support the normal's direction, I'm finding other programs will automatically correct the normal.
Where I had this:
//gather the 3 points for the face and the normal
p1 = vertexes[indexes[v]];
p2 = vertexes[indexes[v + 1]];
p3 = vertexes[indexes[v + 2]];
n = CalculateInvertedSurfaceNormal(p1, p2, p3);
I instead reversed the direction of the points by changed to this:
//gather the 3 points for the face and the normal
p3 = vertexes[indexes[v]];
p2 = vertexes[indexes[v + 1]];
p1 = vertexes[indexes[v + 2]];
n = CalculateInvertedSurfaceNormal(p1, p2, p3);
That solved my problem.
Related
I need to do a boolean subtraction between two models in C#. One of the meshs will be entirely within the other mesh, so I was hoping to reverse the normals for the one model and add the two models together. I am at a loss on how to reverse the normals though.
This is how I'm calculating a surface normal:
//creates surface normals
Vector3D CalculateSurfaceNormal(Point3D p1, Point3D p2, Point3D p3)
{
Vector3D v1 = new Vector3D(0, 0, 0);
Vector3D v2 = new Vector3D(0, 0, 0);
Vector3D normal = new Vector3D(0, 0, 0);
// Finds The Vector Between 2 Points By Subtracting
// The x,y,z Coordinates From One Point To Another.
// Calculate The Vector From Point 2 To Point 1
v1.X = p1.X - p2.X;
v1.Y = p1.Y - p2.Y;
v1.Z = p1.Z - p2.Z;
// Calculate The Vector From Point 3 To Point 2
v2.X = p2.X - p3.X;
v2.Y = p2.Y - p3.Y;
v2.Z = p2.Z - p3.Z;
// Compute The Cross Product To Give Us A Surface Normal
normal.X = v1.Y * v2.Z - v1.Z * v2.Y; // Cross Product For Y - Z
normal.Y = v1.Z * v2.X - v1.X * v2.Z; // Cross Product For X - Z
normal.Z = v1.X * v2.Y - v1.Y * v2.X; // Cross Product For X - Y
normal.Normalize();
return normal;
}
Is there a simple way to invert the surface normal?
To flip a normal just negate it (i.e. by negating each component). This will reverse the direction it points in.
normal.X = -normal.X
normal.Y = -normal.Y
normal.Z = -normal.Z
A vector type will likely have an overloaded unary negation operator for this, so if you've got that you might be able to do simply
normal = -normal
If you're in an environment with backface culling, you'll want to reverse the winding of the vertices of each triangle (which will naturally flip the resulting computed normal). To do this just flip the order of any two vertices of the triangle.
I have a situation, I have 2 bodies and let's suppose one of them is in T-Pose and the other is idle, with it's arms looking down.
I have to find which rotation was applied to your elbow, for example, to match first body's (elbow-to-hand bone) with second body's (elbow-to-hand bone).
I thought about taking three vertex's (they have almost the same meshes, so that's not a problem but I cannot use vertex's normals bcause they're not the same).
Then the thing is, I have three points forming a triangle on first body and 3 points forming another triangle on second body and I want to find which rotation needs to be applied to one of the triangles to match the other.
If it helps, I'm doing it with unity and c#.
EDIT: IMPORTANT! triangles might not have the same dimensions but they are 2d so I only need to know it's rotation
If I understand your problem correctly, you need to find the transformation between two configurations of the same triangle in 2D: {v1,v2,v3} and {w1, w2,w3}. I assume you know vertex correspondance, for example v1->w1, v2->w2, v3->w3.
Here is what you can do:
1. find rotation angle between the two configurations.
2. rotate a point, for example v1 and obtain rotatedV1
3. find translation between rotatedV1 and w1.
Here is the code. For the sake of simplicity, I used WPF struct Vector3D. The final result is a standard double array collecting a 4x4 transformation matrix
static void miniTest() {
// first triangle
Vector3D v1 = new Vector3D();
Vector3D v2 = new Vector3D(2, 0, 0);
Vector3D v3 = new Vector3D(2, 3, 0);
Vector3D w1 = new Vector3D(0,-1, 0);
Vector3D w2 = new Vector3D(0 , -3, 0);
Vector3D w3 = new Vector3D(3, -1, 0);
double[,] transofrmation = getTrMatrix(v1, v2, v3, w1, w2, w3);
}
public static double[,] getTrMatrix(Vector3D V1, Vector3D V2, Vector3D V3, Vector3D W1, Vector3D W2, Vector3D W3) {
Vector3D s1 = V2 - V1;
s1.Normalize();
Vector3D z1 = W2 - W1;
z1.Normalize();
double angle = Math.Acos(Vector3D.DotProduct(s1, z1));
double[,] rotT = new double[,] {
{ Math.Cos(angle), -Math.Sin(angle), 0},
{ Math.Sin(angle), Math.Cos(angle), 0},
{ 0,0,1},
};
double[] rotatedV1 = multiply(rotT, V1);
Vector3D translation = new Vector3D( W1.X - rotatedV1[0], W1.Y - rotatedV1[1], W1.Z - rotatedV1[2]);
double[,] T = new double[,] {
{ Math.Cos(angle), -Math.Sin(angle), 0, translation.X},
{ Math.Sin(angle), Math.Cos(angle), 0, translation.Y},
{ 0,0,1, translation.Z},
{0,0,0,1 } };
return T;
}
// apply rotation matrix to vector
static double[] multiply(double[,] rotMat, Vector3D vec) {
double[] result = new double[3];
result[0] = rotMat[0, 0] * vec.X + rotMat[0, 1] * vec.Y + rotMat[0, 2] * vec.Z;
result[1] = rotMat[1, 0] * vec.X + rotMat[1, 1] * vec.Y + rotMat[1, 2] * vec.Z;
result[1] = rotMat[2, 0] * vec.X + rotMat[2, 1] * vec.Y + rotMat[2, 2] * vec.Z;
return result;
}
I am working with point3D and vector3D classes and I need some help adjusting a point by a given distance.
Point A - point residing at coordinate 0,0,0.
Point B - point residing at coordinate 1,1,1.
Vector AB - vector AB which tells me the length between the two points A and B is distance = 1.73205078.
Code:
Point3D A = new Point3D { X = 0, Y = 0, Z = 0 };
Point3D B = new Point3D { X = 1, Y = 1, Z = 1 };
Vector3D AtoB = A - B;
Double distanceBetweenAandB = AtoB.Length; // the distance will be 1.73205078 here.
I would like to adjust point B. I would like to reduce the distance between point A and point B to 0.5 instead of 1 (adjusting to position C as shown in the diagram). I am trying to work out how to do this.
Point A (0,0,0) is known, point B (1,1,1) is known and the distance to adjust by is known (0.5). How do I calculate?
Pseudo code:
Point3D A = new Point3D { X = 0, Y = 0, Z = 0 };
Point3D B = new Point3D { X = 1, Y = 1, Z = 1 };
Double distanceToAdjust = 0.5;
Point3D newCoordinate = B - distanceToAdjust; // this doesnt work!
Adjusted point B shown in diagram below:
I am using my own defined Point3D class and Vector3D class.
Let's assume your given parameters for your points, and create a 3rd, which we'll call newCoordinate, and that point A will be your reference:
Point3D A = new Point3D { X = 0, Y = 0, Z = 0 };
Point3D B = new Point3D { X = 1, Y = 1, Z = 1 };
Double distanceToAdjust = 0.5;
Point3D newCoordinate = new Point3D {
A.X + ((B.X - A.X) * distanceToAdjust),
A.Y + ((B.Y - A.Y) * distanceToAdjust),
A.Z + ((B.Z - A.Z) * distanceToAdjust)
}
Here we see the original points:
Assuming this values, newCoordinate would sit at X=0.5, Y=0.5, Z=0.5. Nice graph follows:
There it is, sitting right in between the two original points.
As a simulation, if you change A and B and assume this values instead:
Point3D A = new Point3D { X = -8, Y = 4, Z = 3 };
Point3D B = new Point3D { X = 3, Y = 2, Z = 1 };
Then newCoordinate position would be X=-2.5, Y=3, Z=2.
Now, same points, but using distanceToAdjust = 1.2:
Keep this two things in mind:
Changes in distance always need a reference point. In my sample, I assumed A; that's why it appears as the first portion of each newCoordinate parameter initialization.
distanceToAdjust was taken as a multiplier factor.
Addendum: The nifty tool I used to help visualization can be found here.
Assuming you implemented vector operations:
if point A is always [0,0,0]
Point3D new = B.Normalize() * distance;
for any two points
Point3D newCoord = A + ((B - A).Normalize() * distance); //move to origin, normalize, scale and move back
not fast solution though.
"the length between the two points A and B is distance = 1"
No, the distance is the square root of three, about 1.732.
The distance from (0,0,0) to (0,0,1) is 1. The distance from (0,0,0) to (0,1,1) is the square root of two. (Think a triangle in two dimensions, and Pythagoas theorem.) The distance from (0,0,0) to (1,1,1) is the square root of three. (Think a triangle in two dimensions, where that dimension is on a plane along the hypothenuse of the previous triangle. AB = √(1² + (√2)²).)
I assume that you don't want to subtract 0.5 from anything, but actually multiply the distance by 0.5, i.e. getting halfways from A to B. You can calculate the point C by taking that part of the distance between point A and point B in each dimension:
Point3D C = new Point3D {
A.X + (B.X - A.X) * distanceToAdjust,
A.Y + (B.Y - A.Y) * distanceToAdjust,
A.Z + (B.Z - A.Z) * distanceToAdjust
};
In pseudo code, here's how I ended up implementing
pointA = …
pointB = …
vectorAB = B-A
desiredDistance = 0.5; // where 0.5 is vectorAB.Length/desiredDistance
vectorAC = vectorAB * desiredDistance ;
pointC = A+vectorAC;
Actual code:
Vector3D pointC = (Vector3D)(A + (float)desiredDistance * (B - A));
I'm unsure if this is what you would need but is it possible to create a method within your Point3D class to allow subtraction/addition?
(Just guessing the Point3D class as simply as it could be)Something like
public class Point3D
{
public double X,Y,Z
public void ChangeCord(Point3D point)
{
X =- point.X;
Y =- point.Y;
Z =- point.Z;
}
}
So it could just be:
Point3D A = new Point3D { X = 0, Y = 0, Z = 0 };
Point3D B = new Point3D { X = 1, Y = 1, Z = 1 };
Double distanceToAdjust = 0.5;
Point3D newCoordinate = B.ChangeCord(new Point3d{ X = 0.5, Y = 0.5, Z = 0.5 });
If I have three points and always want the visible face should be the side that is "facing" from origo, is there a shortcut to calculate the normal of the plane ?
Like this
mesh.Positions.Add(p0);
mesh.Positions.Add(p1);
mesh.Positions.Add(p2);
mesh.TriangleIndices.Add(0);
mesh.TriangleIndices.Add(1);
mesh.TriangleIndices.Add(2);
normal = Vector3D(1,1,1);
mesh.Normals.Add(normal);
mesh.Normals.Add(normal);
mesh.Normals.Add(normal);
model = new GeometryModel3D(mesh, material);
Or do I have to calculate the normal every time ?
If I have to calculate the normal, what is the algorithm for that, I have looked on the internet and tried a couple methods but they make me suspicious, like this one.
normal = CalculateNormal(p0, p1, p2);
Where CalculateNormal is
public static Vector3D CalculateNormal(Point3D p0, Point3D p1, Point3D p2)
{
Vector3D v0 = new Vector3D(p1.X - p0.X, p1.Y - p0.Y, p1.Z - p0.Z);
Vector3D v1 = new Vector3D(p1.X - p2.X, p1.Y - p2.Y, p2.Z - p1.Z);
return Vector3D.CrossProduct(v0, v1);
}
should it not be
Vector3D v1 = new Vector3D(p1.X - p2.X, p1.Y - p2.Y, p1.Z - p2.Z);
instead ?
/Stefan
The following works well
private Model3DGroup CreateTriangleSide(Point3D p0, Point3D p1, Point3D p2, Material material)
{
MeshGeometry3D mesh = null;
GeometryModel3D model = null;
Model3DGroup group = new Model3DGroup();
Vector3D normal;
//
// Front side of jagged part
//
mesh = new MeshGeometry3D();
mesh.Positions.Add(p0);
mesh.Positions.Add(p1);
mesh.Positions.Add(p2);
mesh.TriangleIndices.Add(0);
mesh.TriangleIndices.Add(1);
mesh.TriangleIndices.Add(2);
normal = CalculateNormal(p0, p1, p2);
normal = Normalize(normal);
mesh.Normals.Add(normal);
mesh.Normals.Add(normal);
mesh.Normals.Add(normal);
model = new GeometryModel3D(mesh, material);
group.Children.Add(model);
//
// Front side of the surface below the jagged edge
//
Point3D p3 = new Point3D(p1.X, p1.Y, bh);
Point3D p4 = new Point3D(p2.X, p2.Y, bh);
return group;
}
public const double RADDEGC = (Math.PI / 180.0);
public enum ANGLETYPE { RAD, DEG };
/*
* Takes the angle and the Z value to create a 3D point in space
*
* #param angle The angle
* #param radius The radius of the circle
* #param z The z value
* #param t The angle type, for example RAD (radians) or DEG (degress
*
*/
public static Point3D CP(double radius, double angle, double z = 0, ANGLETYPE angtype = ANGLETYPE.RAD)
{
Point3D p = new Point3D();
p.Z = z;
//
if (angtype == ANGLETYPE.RAD)
{
p.X = radius * Math.Cos(angle);
p.Y = radius * Math.Sin(angle);
}
else
{
p.X = radius * Math.Cos(angle * RADDEGC);
p.Y = radius * Math.Sin(angle * RADDEGC);
}
return p;
}
public static Vector3D CalculateNormal(Point3D p0, Point3D p1, Point3D p2)
{
Vector3D a1 = new Vector3D(p1.X - p0.X, p1.Y - p0.Y, p1.Z - p0.Z);
Vector3D b1 = new Vector3D(p2.X - p0.X, p2.Y - p0.Y, p2.Z - p0.Z);
Vector3D dir = Vector3D.CrossProduct(a1, b1);
return dir;
}
public static Vector3D Normalize(Vector3D norm)
{
double fac1 = Math.Sqrt((norm.X * norm.X) + (norm.Y * norm.Y) + (norm.Z * norm.Z));
if (fac1 == 0)
{
return norm;
}
norm = new Vector3D(norm.X / fac1, norm.Y / fac1, norm.Z / fac1);
return norm;
}
To get the line of intersection between two rectangles in 3D, I converted them to planes, then get the line of intersection using cross product of their normals, then I try to get the line intersection with each line segment of the rectangle.
The problem is the line is parallel to three segments, and intersect with only one in NAN,NAN,NAN which is totally wrong. Can you advise me what's wrong in my code?
I use vector3 from this link http://www.koders.com/csharp/fidCA8558A72AF7D3E654FDAFA402A168B8BC23C22A.aspx
and created my plane class as following
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace referenceLineAlgorithm
{
struct Line
{
public Vector3 direction;
public Vector3 point;
}
struct lineSegment
{
public Vector3 firstPoint;
public Vector3 secondPoint;
}
class plane_test
{
public enum Line3DResult
{
Line3DResult_Parallel = 0,
Line3DResult_SkewNoCross = 1,
Line3DResult_SkewCross = 2
};
#region Fields
public Vector3 Normal;
public float D;
public Vector3[] cornersArray;
public Vector3 FirstPoint;
public Vector3 SecondPoint;
public Vector3 temp;
public Vector3 normalBeforeNormalization;
#endregion
#region constructors
public plane_test(Vector3 point0, Vector3 point1, Vector3 point2, Vector3 point3)
{
Vector3 edge1 = point1 - point0;
Vector3 edge2 = point2 - point0;
Normal = edge1.Cross(edge2);
normalBeforeNormalization = Normal;
Normal.Normalize();
D = -Normal.Dot(point0);
///// Set the Rectangle corners
cornersArray = new Vector3[] { point0, point1, point2, point3 };
}
#endregion
#region Methods
/// <summary>
/// This is a pseudodistance. The sign of the return value is
/// positive if the point is on the positive side of the plane,
/// negative if the point is on the negative side, and zero if the
/// point is on the plane.
/// The absolute value of the return value is the true distance only
/// when the plane normal is a unit length vector.
/// </summary>
/// <param name="point"></param>
/// <returns></returns>
public float GetDistance(Vector3 point)
{
return Normal.Dot(point) + D;
}
public void Intersection(plane_test SecondOne)
{
///////////////////////////// Get the parallel to the line of interrsection (Direction )
Vector3 LineDirection = Normal.Cross(SecondOne.Normal);
float d1 = this.GetDistance(LineDirection);
float d2 = SecondOne.GetDistance(LineDirection);
temp = (LineDirection - (this.Normal * d1) - (SecondOne.Normal * d2));
temp.x = Math.Abs((float)Math.Round((decimal)FirstPoint.x, 2));
temp.y = Math.Abs((float)Math.Round((decimal)FirstPoint.y, 2));
Line line;
line.direction = LineDirection;
line.point = temp;
////////// Line segments
lineSegment AB, BC, CD, DA;
AB.firstPoint = cornersArray[0]; AB.secondPoint = cornersArray[1];
BC.firstPoint = cornersArray[1]; BC.secondPoint = cornersArray[2];
CD.firstPoint = cornersArray[2]; CD.secondPoint = cornersArray[3];
DA.firstPoint = cornersArray[3]; DA.secondPoint = cornersArray[0];
Vector3 r1 = new Vector3(-1, -1, -1);
Vector3 r2 = new Vector3(-1, -1, -1);
Vector3 r3 = new Vector3(-1, -1, -1);
Vector3 r4 = new Vector3(-1, -1, -1);
/*
0,0 |----------------| w,0
| |
| |
0,h |________________| w,h
*/
IntersectionPointBetweenLines(AB, line, ref r1);
IntersectionPointBetweenLines(BC, line, ref r2);
IntersectionPointBetweenLines(CD, line, ref r3);
IntersectionPointBetweenLines(DA, line, ref r4);
List<Vector3> points = new List<Vector3>();
points.Add(r1);
points.Add(r2);
points.Add(r3);
points.Add(r4);
points.RemoveAll(
t => ((t.x == -1) && (t.y == -1) && (t.z == -1))
);
if (points.Count == 2)
{
FirstPoint = points[0];
SecondPoint = points[1];
}
}
public Line3DResult IntersectionPointBetweenLines(lineSegment first, Line aSecondLine, ref Vector3 result)
{
Vector3 p1 = first.firstPoint;
Vector3 n1 = first.secondPoint - first.firstPoint;
Vector3 p2 = aSecondLine.point;
Vector3 n2 = aSecondLine.direction;
bool parallel = AreLinesParallel(first, aSecondLine);
if (parallel)
{
return Line3DResult.Line3DResult_Parallel;
}
else
{
float d = 0, dt = 0, dk = 0;
float t = 0, k = 0;
if (Math.Abs(n1.x * n2.y - n2.x * n1.y) > float.Epsilon)
{
d = n1.x * (-n2.y) - (-n2.x) * n1.y;
dt = (p2.x - p1.x) * (-n2.y) - (p2.y - p1.y) * (-n2.x);
dk = n1.x * (p2.x - p1.x) - n1.y * (p2.y - p1.y);
}
else if (Math.Abs(n1.z * n2.y - n2.z * n1.y) > float.Epsilon)
{
d = n1.z * (-n2.y) - (-n2.z) * n1.y;
dt = (p2.z - p1.z) * (-n2.y) - (p2.y - p1.y) * (-n2.z);
dk = n1.z * (p2.z - p1.z) - n1.y * (p2.y - p1.y);
}
else if (Math.Abs(n1.x * n2.z - n2.x * n1.z) > float.Epsilon)
{
d = n1.x * (-n2.z) - (-n2.x) * n1.z;
dt = (p2.x - p1.x) * (-n2.z) - (p2.z - p1.z) * (-n2.x);
dk = n1.x * (p2.x - p1.x) - n1.z * (p2.z - p1.z);
}
t = dt / d;
k = dk / d;
result = n1 * t + p1;
// Check if the point on the segmaent or not
// if (! isPointOnSegment(first, result))
//{
// result = new Vector3(-1,-1,-1);
// }
return Line3DResult.Line3DResult_SkewCross;
}
}
private bool AreLinesParallel(lineSegment first, Line aSecondLine)
{
Vector3 vector = (first.secondPoint - first.firstPoint);
vector.Normalize();
float kl = 0, km = 0, kn = 0;
if (vector.x != aSecondLine.direction.x)
{
if (vector.x != 0 && aSecondLine.direction.x != 0)
{
kl = vector.x / aSecondLine.direction.x;
}
}
if (vector.y != aSecondLine.direction.y)
{
if (vector.y != 0 && aSecondLine.direction.y != 0)
{
km = vector.y / aSecondLine.direction.y;
}
}
if (vector.z != aSecondLine.direction.z)
{
if (vector.z != 0 && aSecondLine.direction.z != 0)
{
kn = vector.z / aSecondLine.direction.z;
}
}
// both if all are null or all are equal, the lines are parallel
return (kl == km && km == kn);
}
private bool isPointOnSegment(lineSegment segment, Vector3 point)
{
//(x - x1) / (x2 - x1) = (y - y1) / (y2 - y1) = (z - z1) / (z2 - z1)
float component1 = (point.x - segment.firstPoint.x) / (segment.secondPoint.x - segment.firstPoint.x);
float component2 = (point.y - segment.firstPoint.y) / (segment.secondPoint.y - segment.firstPoint.y);
float component3 = (point.z - segment.firstPoint.z) / (segment.secondPoint.z - segment.firstPoint.z);
if ((component1 == component2) && (component2 == component3))
{
return true;
}
else
{
return false;
}
}
#endregion
}
}
static void Main(string[] args)
{
//// create the first plane points
Vector3 point11 =new Vector3(-255.5f, -160.0f,-1.5f) ; //0,0
Vector3 point21 = new Vector3(256.5f, -160.0f, -1.5f); //0,w
Vector3 point31 = new Vector3(256.5f, -160.0f, -513.5f); //h,0
Vector3 point41 = new Vector3(-255.5f, -160.0f, -513.5f); //w,h
plane_test plane1 = new plane_test(point11, point21, point41, point31);
//// create the Second plane points
Vector3 point12 = new Vector3(-201.6289f, -349.6289f, -21.5f);
Vector3 point22 =new Vector3(310.3711f,-349.6289f,-21.5f);
Vector3 point32 = new Vector3(310.3711f, 162.3711f, -21.5f);
Vector3 point42 =new Vector3(-201.6289f,162.3711f,-21.5f);
plane_test plane2 = new plane_test(point12, point22, point42, point32);
plane2.Intersection(plane1);
}
and this is test values
Best regards
You need to specify one thing first:
by 3D rectangle, you mean plane rectangle on a 3D plane. (not a
rectangular prism).
Let's say your rectangles are not coplanar nor parallele, and therefore there is one unique line D1 that represents the intersection of the plane described by each rectangle.
Given this assumption their are 4 possible situations for the intersection of 2 rectangles R1 and R2:
(note: sometimes D1 doesn't intersect neither R1 nor R2 and R1 , R2 can be rotated a little bit so D1 doesn't always intersect on parallele sides, but consecutive sides)
When there is an intersection between the 2 rectangles, D1 always intersect R1 and R2 on the same intersection (cf 1st and 2nd picture)
Your model is not good because your line cannot be parallele to 3 segments of the same rectangle...
As you asked in this question : 3D lines intersection algorithm once you have D1 ( Get endpoints of the line segment defined by the intersection of two rectangles ) just determinate the intersection with each segment of the rectangle.(The 4 segments of each rectangles need to be checked)
Then check for common intersection... if you find one then your rectangles intersect.
Sorry it's very hard to directly check the code, but I guess with these peaces of information you should be able to find the error.
Hope it helps.
EDIT:
define a rectangle by a point and 2 vectors :
R2 {A ,u ,v}
R1 {B, u',v'}
define the planes described by R1 and R2 : P1 and P2
One orthogonal vector to P1(resp. P2) is n1 (resp. n2).Let n1 = u ^ v and n2 = u' ^ v' with :
then
P1: n1.(x-xA,y-yA,z-zA)=0
P2: n2.(x-xB,y-yB,z-zB)=0
Then if you're just looking for D1 the equation of D1 is :
D1: P1^2 + P2 ^2 =0 (x,y,z verify P1 =0 an P2 =0 )
D1 : n1.(x-xA,y-yA,z-zA)^2 + n2.(x-xB,y-yB,z-zB)^2 =0
(so just with the expression of your rectangles you can get the equation of D1 with a closed formula.)
Now let's look at the intersections :
the 4 points in R1 are :
{ A , A+u , A+v, A+u+v }
as describe in 3D lines intersection algorithm do :
D1 inter [A,A+u] = I1
D1 inter [A,A+v] = I2
D1 inter [A+u,A+u+v] = I3
D1 inter [A+v,A+u+v] = I4
(I1,I2,I3,I4 can be null)
same for D2 you get I1' I2' I3' I4'
if Ij'=Ik' != null then it's an intersection point
if you did that correctly step by step you should get to the correct solution; unless I didn't fully understand the question...
The program computes the line of intersection of the planes passing through two rectangles. The program then looks for intersections between this line and the edges of one of the rectangles. It returns two points of intersection of such two points are found. I'm not going to debate whether this is a sensible thing to do since I don't know the context of the program.
Let's go through the code and look for things that could be wrong.
The program computes the line passing through the two planes like this:
Vector3 LineDirection = Normal.Cross(SecondOne.Normal);
float d1 = this.GetDistance(LineDirection);
float d2 = SecondOne.GetDistance(LineDirection);
temp = (LineDirection - (this.Normal * d1) - (SecondOne.Normal * d2));
temp.x = Math.Abs((float)Math.Round((decimal)FirstPoint.x, 2));
temp.y = Math.Abs((float)Math.Round((decimal)FirstPoint.y, 2));
Line line;
line.direction = LineDirection;
line.point = temp;
The computation of the line direction is OK, but the computation of point is wrong, as you probably know. But I'll pretend we have a valid point and direction and carry on with the rest of the program.
The program calls AreLinesParallel() to get rid of edges that a parallel to the line through the planes. The code looks like this:
Vector3 vector = (first.secondPoint - first.firstPoint);
vector.Normalize();
float kl = 0, km = 0, kn = 0;
if (vector.x != aSecondLine.direction.x)
{
if (vector.x != 0 && aSecondLine.direction.x != 0)
{
kl = vector.x / aSecondLine.direction.x;
}
}
if (vector.y != aSecondLine.direction.y)
{
if (vector.y != 0 && aSecondLine.direction.y != 0)
{
km = vector.y / aSecondLine.direction.y;
}
}
if (vector.z != aSecondLine.direction.z)
{
if (vector.z != 0 && aSecondLine.direction.z != 0)
{
kn = vector.z / aSecondLine.direction.z;
}
}
// both if all are null or all are equal, the lines are parallel
return ((kl == km && km == kn));
The code more or less checks that the elements of the direction of the edge divided by the elements of the direction of the line are all equal to each other. It's a dangerous procedure to rely on. Because of round-off errors, later procedures may still, say, divide by zero, even if AreLinesParallel() claims that the lines aren't really parallel. It is better not to use the procedure at all.
Now comes the meat of the code, a test for intersection between the edge and the line:
float d = 0, dt = 0, dk = 0;
float t = 0, k = 0;
if (Math.Abs(n1.x * n2.y - n2.x * n1.y) > float.Epsilon)
{
d = n1.x * (-n2.y) - (-n2.x) * n1.y;
dt = (p2.x - p1.x) * (-n2.y) - (p2.y - p1.y) * (-n2.x);
dk = n1.x * (p2.x - p1.x) - n1.y * (p2.y - p1.y);
}
else if (Math.Abs(n1.z * n2.y - n2.z * n1.y) > float.Epsilon)
{
d = n1.z * (-n2.y) - (-n2.z) * n1.y;
dt = (p2.z - p1.z) * (-n2.y) - (p2.y - p1.y) * (-n2.z);
dk = n1.z * (p2.z - p1.z) - n1.y * (p2.y - p1.y);
}
else if (Math.Abs(n1.x * n2.z - n2.x * n1.z) > float.Epsilon)
{
d = n1.x * (-n2.z) - (-n2.x) * n1.z;
dt = (p2.x - p1.x) * (-n2.z) - (p2.z - p1.z) * (-n2.x);
dk = n1.x * (p2.x - p1.x) - n1.z * (p2.z - p1.z);
}
t = dt / d;
k = dk / d;
result = n1 * t + p1;
A mistake of this code is the lack of a comment that explains the origin of the algorithm. If there is no documented algorithm to refer to, the comment can contain the derivation leading to the formulas. The first branch deals with (x, y), the second with (y, z) and the third with (z, x), so I assume that the branches solve for intersection in 2D and lift these findings to 3D. It computes determinants to check for parallel lines for each 2D projection. I shouldn't have to do this kind of reverse engineering.
Anyway, this is the code that produces the NaN values. None of the three branches are triggered, so d = 0 in the end, which gives a division by zero. Instead of relying on AreLinesParallel() to avoid division by zero, it's is better to check the value that actually matters, namely d.
Of course, the code still needs more work, because we don't know yet if the lines are crossing in 3D too. Also the point is on the edge only if 0 <= t && t <= 1. And probably more bugs will show up as the earlier ones are being fixed.