/// <summary> /// Calculating an intersection between a Face and a LineSegment /// </summary> /// <param name="F1">the Face3D</param> /// <param name="LS">the Line Segment</param> /// <param name="intPoint">the intersection point</param> /// <returns></returns> public static bool intersect(Face3D F1, LineSegment3D LS, out List <Point3D> intPoints) { intPoints = new List <Point3D>(); Point3D intPt = new Point3D(); // There are 2 possible cases: 1. Line punching the face, 2. Line lies on the same plane as the face // Test whether the line is on the same plane if (MathUtils.equalTol(Vector3D.DotProduct(F1.basePlane.normalVector, LS.baseLine.direction), 0.0, MathUtils.defaultTol)) { // test whether at least one point of the segment is on the plane if (!Plane3D.pointOnPlane(F1.basePlane, LS.startPoint)) { return(false); // line is parallel with the plane, no intersection } LineSegmentIntersectEnum mode = LineSegmentIntersectEnum.Undefined; for (int i = 0; i < F1.boundaries.Count; i++) { bool st = LineSegment3D.intersect(F1.boundaries[i], LS, out intPt, out mode); if (st) { intPoints.Add(intPt); } } if (intPoints.Count > 0) { return(true); } return(false); } else { bool res = Plane3D.PLintersect(F1.basePlane, LS, out intPt); if (res == false) { return(false); // intersection occurs beyond the line segment } // There is intersection point, test whether the point in within (inside the boundary of the face boundaries res = inside(F1, intPt); if (res) { intPoints.Add(intPt); return(true); } } return(false); }
/// <summary> /// Test a point is inside a face /// </summary> /// <param name="F1"></param> /// <param name="P1"></param> /// <returns></returns> public static bool inside(Face3D F1, Point3D P1) { if (!Plane3D.pointOnPlane(F1.basePlane, P1)) { return(false); // Point is not on a plane } // test Point inside a Face // Need to project the plane to 2D (XY-, YZ-, or XZ- plane). It should work well with convex face (esp. triangles and rectangles we will deal with mostly) double maxDim = Double.MinValue; int DimttoZero = 0; // First test whether the plane is already axis-aligned (in this case we can just remove the appropriate axis) if (MathUtils.equalTol(F1.basePlane.normalVector.Y, 0.0, MathUtils.defaultTol) && MathUtils.equalTol(F1.basePlane.normalVector.Z, 0.0, MathUtils.defaultTol)) { DimttoZero = 0; // Ignore X, project to Y-Z plane } else if (MathUtils.equalTol(F1.basePlane.normalVector.X, 0.0, MathUtils.defaultTol) && MathUtils.equalTol(F1.basePlane.normalVector.Z, 0.0, MathUtils.defaultTol)) { DimttoZero = 1; // Ignore Y, project to X-Z plane } else if (MathUtils.equalTol(F1.basePlane.normalVector.X, 0.0, MathUtils.defaultTol) && MathUtils.equalTol(F1.basePlane.normalVector.Y, 0.0, MathUtils.defaultTol)) { DimttoZero = 2; // Ignore Z, project to X-Y plane } else { if (maxDim < Math.Abs(F1.basePlane.normalVector.X)) { maxDim = Math.Abs(F1.basePlane.normalVector.X); DimttoZero = 0; } if (maxDim < Math.Abs(F1.basePlane.normalVector.Y)) { maxDim = Math.Abs(F1.basePlane.normalVector.Y); DimttoZero = 1; } if (maxDim < Math.Abs(F1.basePlane.normalVector.Z)) { maxDim = Math.Abs(F1.basePlane.normalVector.Z); DimttoZero = 2; } } // We ignore the largest component, which means the least impact to the projection plane List <Point3D> projVert = new List <Point3D>(); Point3D projIntP = new Point3D(P1.X, P1.Y, P1.Z); Point3D rayEndP = new Point3D(P1.X, P1.Y, P1.Z); if (DimttoZero == 0) { for (int i = 0; i < F1.vertices.Count; i++) { projVert.Add(new Point3D(0.0, F1.vertices[i].Y, F1.vertices[i].Z)); } projIntP.X = 0.0; rayEndP.X = 0.0; if (Octree.WorldBB == null) { rayEndP.Y += Point3D.distance(F1.containingBB.URT, F1.containingBB.LLB) * 1000; } else { rayEndP.Y += Octree.WorldBB.extent * 2; } } else if (DimttoZero == 1) { for (int i = 0; i < F1.vertices.Count; i++) { projVert.Add(new Point3D(F1.vertices[i].X, 0.0, F1.vertices[i].Z)); } projIntP.Y = 0.0; rayEndP.Y = 0.0; //rayEndP.Z += Point3D.distance(F1.containingBB.URT, F1.containingBB.LLB) * 2; if (Octree.WorldBB == null) { rayEndP.X += Point3D.distance(F1.containingBB.URT, F1.containingBB.LLB) * 2; // Use X axis for the ray } else { rayEndP.X += Octree.WorldBB.extent * 2; } } else if (DimttoZero == 2) { for (int i = 0; i < F1.vertices.Count; i++) { projVert.Add(new Point3D(F1.vertices[i].X, F1.vertices[i].Y, 0.0)); } projIntP.Z = 0.0; rayEndP.Z = 0.0; if (Octree.WorldBB == null) { rayEndP.X += Point3D.distance(F1.containingBB.URT, F1.containingBB.LLB) * 2; } else { rayEndP.X += Octree.WorldBB.extent * 2; } } Face3D projFace = new Face3D(projVert); // define a ray from the intersection point along the X-axis of the projected plane by using long enough line segment beginning from the point, using // max extent of the face containingBB *2 LineSegment3D ray = new LineSegment3D(projIntP, rayEndP); // Now do intersection between the ray and all the segments of the face. Odd number indicates the point is inside // 4 rules to follow: // 1. If the segment is upward, exclude the endpoint for intersection (only consider the startpoint) // 2. If the segment is downward, exclude the startpoint for intersection (only considers the end point) // 3. Ignore the segment that is horizontal (parallel to the ray) // 4. ray is always strictly to the right of the Point int intCount = 0; Point3D iP = new Point3D(); LineSegmentIntersectEnum mod = LineSegmentIntersectEnum.Undefined; for (int i = 0; i < projFace.boundaries.Count; i++) { if (ray.baseLine.direction == projFace.boundaries[i].baseLine.direction) { continue; //ignore segment that is parallel to the ray (rule #3) } Point3D pointToExclude = new Point3D(); if (DimttoZero == 0 || DimttoZero == 1) { // for both X-Z and Y-Z plane (Z as vertical axis) if (projFace.boundaries[i].startPoint.Z <= ray.startPoint.Z && projFace.boundaries[i].endPoint.Z > ray.startPoint.Z) { pointToExclude = projFace.boundaries[i].endPoint; // Rule #1 } else if (projFace.boundaries[i].startPoint.Z > ray.startPoint.Z && projFace.boundaries[i].endPoint.Z <= ray.startPoint.Z) { pointToExclude = projFace.boundaries[i].startPoint; // Rule #2 } } else { // for X-Y plane (Y as vertical axis) if (projFace.boundaries[i].startPoint.Y <= ray.startPoint.Y && projFace.boundaries[i].endPoint.Y > ray.startPoint.Y) { pointToExclude = projFace.boundaries[i].endPoint; // Rule #1 } else if (projFace.boundaries[i].startPoint.Y > ray.startPoint.Y && projFace.boundaries[i].endPoint.Y <= ray.startPoint.Y) { pointToExclude = projFace.boundaries[i].startPoint; // Rule #2 } } // In the evaluation of the number of intersection between a ray and a face, we will ignore the intersection point that is equal to the rule #2 or #3 if (LineSegment3D.intersect(ray, projFace.boundaries[i], out iP, out mod) && (iP != pointToExclude)) { intCount++; } } if (intCount % 2 == 1) { return(true); } return(false); }