//--------------------------------CONSTRUCTORS----------------------------------// /** * Constructs a BooleanModeller object to apply boolean operation in two solids. * Makes preliminary calculations * * @param solid1 first solid where boolean operations will be applied * @param solid2 second solid where boolean operations will be applied */ public BooleanModeller(Solid solid1, Solid solid2) { //representation to apply boolean operations object1 = new Object3D(solid1); object2 = new Object3D(solid2); //split the faces so that none of them intercepts each other object1.splitFaces(object2); object2.splitFaces(object1); //classify faces as being inside or outside the other solid object1.classifyFaces(object2); object2.classifyFaces(object1); }
/// <summary> /// Clones the Object3D object /// </summary> /// <returns>cloned object</returns> public Object3D Clone() { Object3D clone = new Object3D(); clone.vertices = new List<Vertex>(); for (int i = 0; i < vertices.Count; i++) { clone.vertices.Add(vertices[i].Clone()); } clone.faces = new List<Face>(); for (int i = 0; i < vertices.Count; i++) { clone.faces.Add(faces[i].Clone()); } clone.bound = bound; return clone; }
/// <summary> /// Clones the Object3D object /// </summary> /// <returns>cloned object</returns> public Object3D Clone() { Object3D clone = new Object3D(); clone.Vertices = new List <Vertex>(); for (int i = 0; i < Vertices.Count; i++) { clone.Vertices.Add(Vertices[i].Clone()); } clone.Faces = new List <Face>(); for (int i = 0; i < Vertices.Count; i++) { clone.Faces.Add(Faces[i].Clone()); } clone._Bound = _Bound; return(clone); }
/// <summary> /// Classify faces as being inside, outside or on boundary of other object /// </summary> /// <param name="otherObject">object 3d used for the comparison</param> public void ClassifyFaces(Object3D otherObject) { //calculate adjacency information Face face; for (int i = 0; i < this.GetNumFaces(); i++) { face = this.GetFace(i); face.v1.AddAdjacentVertex(face.v2); face.v1.AddAdjacentVertex(face.v3); face.v2.AddAdjacentVertex(face.v1); face.v2.AddAdjacentVertex(face.v3); face.v3.AddAdjacentVertex(face.v1); face.v3.AddAdjacentVertex(face.v2); } //for each face for (int i = 0; i < GetNumFaces(); i++) { face = GetFace(i); //if the face vertices aren't classified to make the simple classify if (face.SimpleClassify() == false) { //makes the ray trace classification face.RayTraceClassify(otherObject); //mark the vertices if (face.v1.GetStatus() == Status.UNKNOWN) { face.v1.Mark(face.GetStatus()); } if (face.v2.GetStatus() == Status.UNKNOWN) { face.v2.Mark(face.GetStatus()); } if (face.v3.GetStatus() == Status.UNKNOWN) { face.v3.Mark(face.GetStatus()); } } } }
//-----------------------------------OTHERS-------------------------------------// /** * Classify faces as being inside, outside or on boundary of other object * * @param object object 3d used for the comparison */ public void classifyFaces(Object3D obj) { //calculate adjacency information Face face; for (int i = 0; i < this.getNumFaces(); i++) { face = this.getFace(i); face.v1.addAdjacentVertex(face.v2); face.v1.addAdjacentVertex(face.v3); face.v2.addAdjacentVertex(face.v1); face.v2.addAdjacentVertex(face.v3); face.v3.addAdjacentVertex(face.v1); face.v3.addAdjacentVertex(face.v2); } //for each face for (int i = 0; i < getNumFaces(); i++) { face = getFace(i); //if the face vertices aren't classified to make the simple classify if (face.simpleClassify() == false) { //makes the ray trace classification face.rayTraceClassify(obj); //mark the vertices if (face.v1.getStatus() == Vertex.UNKNOWN) { face.v1.mark(face.getStatus()); } if (face.v2.getStatus() == Vertex.UNKNOWN) { face.v2.mark(face.getStatus()); } if (face.v3.getStatus() == Vertex.UNKNOWN) { face.v3.mark(face.getStatus()); } } } }
/// <summary> /// 划分自身平面为在其他对象的内部、外部或边界上 /// </summary> /// <param name="otherObject">用来比较的 3D 物件</param> public void ClassifyFaces(Object3D otherObject) { Face face; for (int i = 0; i < GetNumFaces(); i++) // 指定邻接点 { face = GetFace(i); face.v1.AddAdjacentVertex(face.v2); face.v1.AddAdjacentVertex(face.v3); face.v2.AddAdjacentVertex(face.v1); face.v2.AddAdjacentVertex(face.v3); face.v3.AddAdjacentVertex(face.v1); face.v3.AddAdjacentVertex(face.v2); } for (int i = 0; i < GetNumFaces(); i++) // 采样每个面 { face = GetFace(i); if (!face.SimpleClassify()) // 简单分类没有分类的顶点 { face.RayTraceClassify(otherObject); // 用射线采样分类 // 标记顶点 if (face.v1.Status == Status.UNKNOWN) { face.v1.Mark(face.Status); } if (face.v2.Status == Status.UNKNOWN) { face.v2.Mark(face.Status); } if (face.v3.Status == Status.UNKNOWN) { face.v3.Mark(face.Status); } } } }
//-------------------------FACES_SPLITTING_METHODS------------------------------// /** * Split faces so that none face is intercepted by a face of other object * * @param object the other object 3d used to make the split */ public void splitFaces(Object3D obj) { Line line; Face face1, face2; Segment[] segments; Segment segment1; Segment segment2; double distFace1Vert1, distFace1Vert2, distFace1Vert3, distFace2Vert1, distFace2Vert2, distFace2Vert3; int signFace1Vert1, signFace1Vert2, signFace1Vert3, signFace2Vert1, signFace2Vert2, signFace2Vert3; int numFacesBefore = getNumFaces(); int numFacesStart = getNumFaces(); int facesIgnored = 0; //if the objects bounds overlap... if (getBound().overlap(obj.getBound())) { //for each object1 face... for (int i = 0; i < getNumFaces(); i++) { //if object1 face bound and object2 bound overlap ... face1 = getFace(i); if (face1.getBound().overlap(obj.getBound())) { //for each object2 face... for (int j = 0; j < obj.getNumFaces(); j++) { //if object1 face bound and object2 face bound overlap... face2 = obj.getFace(j); if (face1.getBound().overlap(face2.getBound())) { //PART I - DO TWO POLIGONS INTERSECT? //POSSIBLE RESULTS: INTERSECT, NOT_INTERSECT, COPLANAR //distance from the face1 vertices to the face2 plane distFace1Vert1 = computeDistance(face1.v1, face2); distFace1Vert2 = computeDistance(face1.v2, face2); distFace1Vert3 = computeDistance(face1.v3, face2); //distances signs from the face1 vertices to the face2 plane signFace1Vert1 = (distFace1Vert1 > TOL ? 1 : (distFace1Vert1 < -TOL ? -1 : 0)); signFace1Vert2 = (distFace1Vert2 > TOL ? 1 : (distFace1Vert2 < -TOL ? -1 : 0)); signFace1Vert3 = (distFace1Vert3 > TOL ? 1 : (distFace1Vert3 < -TOL ? -1 : 0)); //if all the signs are zero, the planes are coplanar //if all the signs are positive or negative, the planes do not intersect //if the signs are not equal... if (!(signFace1Vert1 == signFace1Vert2 && signFace1Vert2 == signFace1Vert3)) { //distance from the face2 vertices to the face1 plane distFace2Vert1 = computeDistance(face2.v1, face1); distFace2Vert2 = computeDistance(face2.v2, face1); distFace2Vert3 = computeDistance(face2.v3, face1); //distances signs from the face2 vertices to the face1 plane signFace2Vert1 = (distFace2Vert1 > TOL ? 1 : (distFace2Vert1 < -TOL ? -1 : 0)); signFace2Vert2 = (distFace2Vert2 > TOL ? 1 : (distFace2Vert2 < -TOL ? -1 : 0)); signFace2Vert3 = (distFace2Vert3 > TOL ? 1 : (distFace2Vert3 < -TOL ? -1 : 0)); //if the signs are not equal... if (!(signFace2Vert1 == signFace2Vert2 && signFace2Vert2 == signFace2Vert3)) { line = new Line(face1, face2); //intersection of the face1 and the plane of face2 segment1 = new Segment(line, face1, signFace1Vert1, signFace1Vert2, signFace1Vert3); //intersection of the face2 and the plane of face1 segment2 = new Segment(line, face2, signFace2Vert1, signFace2Vert2, signFace2Vert3); //if the two segments intersect... if (segment1.intersect(segment2)) { //PART II - SUBDIVIDING NON-COPLANAR POLYGONS int lastNumFaces = getNumFaces(); this.splitFace(i, segment1, segment2); //prevent from infinite loop (with a loss of faces...) //if(numFacesStart*20<getNumFaces()) //{ // System.out.println("possible infinite loop situation: terminating faces split"); // return; //} //if the face in the position isn't the same, there was a break if (face1 != getFace(i)) { //if the generated solid is equal the origin... if (face1.equals(getFace(getNumFaces() - 1))) { //return it to its position and jump it if (i != (getNumFaces() - 1)) { faces.RemoveAt(getNumFaces() - 1); faces.Insert(i, face1); } else { continue; } } //else: test next face else { i--; break; } } } } } } } } } } }
/** * Classifies the face based on the ray trace technique * * @param object object3d used to compute the face status */ public void rayTraceClassify(Object3D obj) { //creating a ray starting starting at the face baricenter going to the normal direction Point3d p0 = new Point3d(); p0.X = (v1.X + v2.X + v3.X) / 3.0f; p0.Y = (v1.Y + v2.Y + v3.Y) / 3.0f; p0.Z = (v1.Z + v2.Z + v3.Z) / 3.0f; Line ray = new Line(getNormal(), p0); bool success; double dotProduct, distance; Point3d?intersectionPoint; Face closestFace = null; double closestDistance; do { success = true; closestDistance = Double.MaxValue; //for each face from the other solid... for (int i = 0; i < obj.getNumFaces(); i++) { Face face = obj.getFace(i); //dotProduct = face.getNormal().dot(ray.getDirection()); dotProduct = Vector3d.Dot(face.getNormal(), ray.getDirection()); intersectionPoint = ray.computePlaneIntersection(face.getNormal(), face.v1.getPosition()); //if ray intersects the plane... if (intersectionPoint != null) { distance = ray.computePointToPointDistance(intersectionPoint.Value); //if ray lies in plane... if (Math.Abs(distance) < TOL && Math.Abs(dotProduct) < TOL) { //disturb the ray in order to not lie into another plane ray.perturbDirection(); success = false; break; } //if ray starts in plane... if (Math.Abs(distance) < TOL && Math.Abs(dotProduct) > TOL) { //if ray intersects the face... if (face.hasPoint(intersectionPoint.Value)) { //faces coincide closestFace = face; closestDistance = 0; break; } } //if ray intersects plane... else if (Math.Abs(dotProduct) > TOL && distance > TOL) { if (distance < closestDistance) { //if ray intersects the face; if (face.hasPoint(intersectionPoint.Value)) { //this face is the closest face untill now closestDistance = distance; closestFace = face; } } } } } } while (success == false); //none face found: outside face if (closestFace == null) { status = OUTSIDE; } //face found: test dot product else { //dotProduct = closestFace.getNormal().dot(ray.getDirection()); dotProduct = Vector3d.Dot(closestFace.getNormal(), ray.getDirection()); //distance = 0: coplanar faces if (Math.Abs(closestDistance) < TOL) { if (dotProduct > TOL) { status = SAME; } else if (dotProduct < -TOL) { status = OPPOSITE; } } //dot product > 0 (same direction): inside face else if (dotProduct > TOL) { status = INSIDE; } //dot product < 0 (opposite direction): outside face else if (dotProduct < -TOL) { status = OUTSIDE; } } }
/// <summary> /// Split faces so that none face is intercepted by a face of other object /// </summary> /// <param name="compareObject">the other object 3d used to make the split</param> public void SplitFaces(Object3D compareObject) { Line line; Face thisFace, compareFace; Segment segment1; Segment segment2; double v1DistToCompareFace, distFace1Vert2, distFace1Vert3, distFace2Vert1, distFace2Vert2, distFace2Vert3; int signFace1Vert1, signFace1Vert2, signFace1Vert3, signFace2Vert1, signFace2Vert2, signFace2Vert3; int numFacesBefore = this.GetNumFaces(); int numFacesStart = this.GetNumFaces(); //if the objects bounds overlap... if (this.GetBound().Overlap(compareObject.GetBound())) { //for each object1 face... for (int thisFaceIndex = 0; thisFaceIndex < this.GetNumFaces(); thisFaceIndex++) { //if object1 face bound and object2 bound overlap ... thisFace = GetFace(thisFaceIndex); if (thisFace.GetBound().Overlap(compareObject.GetBound())) { //for each object2 face... for (int compareFaceIndex = 0; compareFaceIndex < compareObject.GetNumFaces(); compareFaceIndex++) { //if object1 face bound and object2 face bound overlap... compareFace = compareObject.GetFace(compareFaceIndex); if (thisFace.GetBound().Overlap(compareFace.GetBound())) { //PART I - DO TWO POLIGONS INTERSECT? //POSSIBLE RESULTS: INTERSECT, NOT_INTERSECT, COPLANAR //distance from the face1 vertices to the face2 plane v1DistToCompareFace = ComputeDistance(thisFace.v1, compareFace); distFace1Vert2 = ComputeDistance(thisFace.v2, compareFace); distFace1Vert3 = ComputeDistance(thisFace.v3, compareFace); //distances signs from the face1 vertices to the face2 plane signFace1Vert1 = (v1DistToCompareFace > EqualityTolerance ? 1 : (v1DistToCompareFace < -EqualityTolerance ? -1 : 0)); signFace1Vert2 = (distFace1Vert2 > EqualityTolerance ? 1 : (distFace1Vert2 < -EqualityTolerance ? -1 : 0)); signFace1Vert3 = (distFace1Vert3 > EqualityTolerance ? 1 : (distFace1Vert3 < -EqualityTolerance ? -1 : 0)); //if all the signs are zero, the planes are coplanar //if all the signs are positive or negative, the planes do not intersect //if the signs are not equal... if (!(signFace1Vert1 == signFace1Vert2 && signFace1Vert2 == signFace1Vert3)) { //distance from the face2 vertices to the face1 plane distFace2Vert1 = ComputeDistance(compareFace.v1, thisFace); distFace2Vert2 = ComputeDistance(compareFace.v2, thisFace); distFace2Vert3 = ComputeDistance(compareFace.v3, thisFace); //distances signs from the face2 vertices to the face1 plane signFace2Vert1 = (distFace2Vert1 > EqualityTolerance ? 1 : (distFace2Vert1 < -EqualityTolerance ? -1 : 0)); signFace2Vert2 = (distFace2Vert2 > EqualityTolerance ? 1 : (distFace2Vert2 < -EqualityTolerance ? -1 : 0)); signFace2Vert3 = (distFace2Vert3 > EqualityTolerance ? 1 : (distFace2Vert3 < -EqualityTolerance ? -1 : 0)); //if the signs are not equal... if (!(signFace2Vert1 == signFace2Vert2 && signFace2Vert2 == signFace2Vert3)) { line = new Line(thisFace, compareFace); //intersection of the face1 and the plane of face2 segment1 = new Segment(line, thisFace, signFace1Vert1, signFace1Vert2, signFace1Vert3); //intersection of the face2 and the plane of face1 segment2 = new Segment(line, compareFace, signFace2Vert1, signFace2Vert2, signFace2Vert3); //if the two segments intersect... if (segment1.Intersect(segment2)) { //PART II - SUBDIVIDING NON-COPLANAR POLYGONS int lastNumFaces = GetNumFaces(); bool splitOccured = this.SplitFace(thisFaceIndex, segment1, segment2); //prevent from infinite loop (with a loss of faces...) if (GetNumFaces() > numFacesStart * 100) { //System.out.println("possible infinite loop situation: terminating faces split"); //return; int a = 0; } //if the face in the position isn't the same, there was a break if (splitOccured && thisFace != GetFace(thisFaceIndex)) { //if the generated solid is equal the origin... if (thisFace.Equals(GetFace(GetNumFaces() - 1))) { //return it to its position and jump it if (thisFaceIndex != (GetNumFaces() - 1)) { faces.RemoveAt(GetNumFaces() - 1); faces.Insert(thisFaceIndex, thisFace); } else { continue; } } //else: test next face else { thisFaceIndex--; break; } } } } } } } } } } }
/// <summary> /// 为表面分类,基于光线追踪技术 /// </summary> /// <param name="obj">计算面状态的 3D 物件</param> public void RayTraceClassify(Object3D obj) { Line ray = new Line(PlaneNormal, Center); // 射线从面的几何中心发出,方向为法线方向 Face closet = default; bool foundCloset = false; double closestDistance; bool success; do { success = true; closestDistance = double.MaxValue; for (int faceIndex = 0; faceIndex < obj.GetNumFaces(); faceIndex++) { Face face = obj.GetFace(faceIndex); var intersectionPoint = ray.ComputePlaneIntersection(face.Plane); // 对三角面所在的平面采样 if (intersectionPoint.x < float.PositiveInfinity) // 射线被平面拦截 { var distance = ray.ComputePointToPointDistance(intersectionPoint); var dot = Vector3Double.Dot(face.PlaneNormal, ray.Direction); bool parallel = Math.Abs(dot) < epsilon; // 射线与平面平行 //if ray lies in plane... if (Math.Abs(distance) < epsilon) // 交点是射线起点 { if (parallel) { // 略微抖动光线方向以免采样到另一平面 ray.PerturbDirection(); success = false; break; } else if (face.ContainsPoint(intersectionPoint)) { // 面重合 closet = face; foundCloset = true; closestDistance = 0; break; } } else if (!parallel && distance > epsilon) // 射线产生交点 { if (distance < closestDistance && face.ContainsPoint(intersectionPoint)) { closestDistance = distance; closet = face; // 当前的面时最近的平面 foundCloset = true; } } } } } while (!success); if (!foundCloset) { Status = Status.OUTSIDE; } // 没有找到面,自己是外部面 else // 由离自己最近的面,检查方向 { var dot = Vector3Double.Dot(closet.PlaneNormal, ray.Direction); if (Math.Abs(closestDistance) < epsilon) // 距离为零,这个面和自己重合 { if (dot > epsilon) { Status = Status.SAME; } else if (dot < -epsilon) { Status = Status.OPPOSITE; } } else if (dot > epsilon) { Status = Status.INSIDE; } // 不重合,同向,在参数物件内部 else if (dot < -epsilon) { Status = Status.OUTSIDE; } // 不重合,反向,在参数物件外部 } }
//-----------------------------------OVERRIDES----------------------------------// /** * Clones the Object3D object * * @return cloned Object3D object */ public Object3D Clone() { Object3D clone = new Object3D(); clone.vertices = new List<Vertex>(); for (int i = 0; i < vertices.Count; i++) { clone.vertices.Add(vertices[i].Clone()); } clone.faces = new List<Face>(); for (int i = 0; i < vertices.Count; i++) { clone.faces.Add(faces[i].Clone()); } clone.bound = bound; return clone; }
/// <summary> /// Split faces so that none face is intercepted by a face of other object /// </summary> /// <param name="obj">the other object 3d used to make the split</param> public void SplitFaces(Object3D obj) { Line line; Face face1, face2; Segment segment1; Segment segment2; double distFace1Vert1, distFace1Vert2, distFace1Vert3, distFace2Vert1, distFace2Vert2, distFace2Vert3; int signFace1Vert1, signFace1Vert2, signFace1Vert3, signFace2Vert1, signFace2Vert2, signFace2Vert3; int numFacesBefore = NumFaces; int numFacesStart = NumFaces; //if the objects bounds overlap... if (_Bound.Overlap(obj._Bound)) { //for each object1 face... for (int i = 0; i < NumFaces; i++) { //if object1 face bound and object2 bound overlap ... face1 = GetFace(i); if (face1.GetBound().Overlap(obj._Bound)) { //for each object2 face... for (int j = 0; j < obj.NumFaces; j++) { //if object1 face bound and object2 face bound overlap... face2 = obj.GetFace(j); if (face1.GetBound().Overlap(face2.GetBound())) { //PART I - DO TWO POLIGONS INTERSECT? //POSSIBLE RESULTS: INTERSECT, NOT_INTERSECT, COPLANAR //distance from the face1 vertices to the face2 plane distFace1Vert1 = ComputeDistance(face1.V1, face2); distFace1Vert2 = ComputeDistance(face1.V2, face2); distFace1Vert3 = ComputeDistance(face1.V3, face2); //distances signs from the face1 vertices to the face2 plane signFace1Vert1 = distFace1Vert1 > EqualityTolerance ? 1 : (distFace1Vert1 < -EqualityTolerance ? -1 : 0); signFace1Vert2 = distFace1Vert2 > EqualityTolerance ? 1 : (distFace1Vert2 < -EqualityTolerance ? -1 : 0); signFace1Vert3 = distFace1Vert3 > EqualityTolerance ? 1 : (distFace1Vert3 < -EqualityTolerance ? -1 : 0); //if all the signs are zero, the planes are coplanar //if all the signs are positive or negative, the planes do not intersect //if the signs are not equal... if (!(signFace1Vert1 == signFace1Vert2 && signFace1Vert2 == signFace1Vert3)) { //distance from the face2 vertices to the face1 plane distFace2Vert1 = ComputeDistance(face2.V1, face1); distFace2Vert2 = ComputeDistance(face2.V2, face1); distFace2Vert3 = ComputeDistance(face2.V3, face1); //distances signs from the face2 vertices to the face1 plane signFace2Vert1 = distFace2Vert1 > EqualityTolerance ? 1 : (distFace2Vert1 < -EqualityTolerance ? -1 : 0); signFace2Vert2 = distFace2Vert2 > EqualityTolerance ? 1 : (distFace2Vert2 < -EqualityTolerance ? -1 : 0); signFace2Vert3 = distFace2Vert3 > EqualityTolerance ? 1 : (distFace2Vert3 < -EqualityTolerance ? -1 : 0); //if the signs are not equal... if (!(signFace2Vert1 == signFace2Vert2 && signFace2Vert2 == signFace2Vert3)) { line = new Line(face1, face2); //intersection of the face1 and the plane of face2 segment1 = new Segment(line, face1, signFace1Vert1, signFace1Vert2, signFace1Vert3); //intersection of the face2 and the plane of face1 segment2 = new Segment(line, face2, signFace2Vert1, signFace2Vert2, signFace2Vert3); //if the two segments intersect... if (segment1.Intersect(segment2)) { //PART II - SUBDIVIDING NON-COPLANAR POLYGONS int lastNumFaces = NumFaces; SplitFace(i, segment1, segment2); //prevent from infinite loop (with a loss of faces...) // TODO: Determine a ideal face number. // 20 is too less! // 87 is required for the Sample Application // 1000 to be sure // Is there a better way? if (numFacesStart * 1000 < NumFaces) { Console.WriteLine("possible infinite loop situation: terminating faces split"); return; } //if the face in the position isn't the same, there was a break if (face1 != GetFace(i)) { //if the generated solid is equal the origin... if (face1.Equals(GetFace(NumFaces - 1))) { //return it to its position and jump it if (i != (NumFaces - 1)) { Faces.RemoveAt(NumFaces - 1); Faces.Insert(i, face1); } else { continue; } } //else: test next face else { i--; break; } } } } } } } } } } }
/** * Fills solid arrays with data about faces of an object generated whose status * is as required * * @param object3d solid object used to fill the arrays * @param vertices vertices array to be filled * @param indices indices array to be filled * @param colors colors array to be filled * @param faceStatus1 a status expected for the faces used to to fill the data arrays * @param faceStatus2 a status expected for the faces used to to fill the data arrays */ private void groupObjectComponents(Object3D obj, List<Vertex> vertices, List<int> indices, List<Color3f> colors, int faceStatus1, int faceStatus2) { Face face; //for each face.. for (int i = 0; i < obj.getNumFaces(); i++) { face = obj.getFace(i); //if the face status fits with the desired status... if (face.getStatus() == faceStatus1 || face.getStatus() == faceStatus2) { //adds the face elements into the arrays Vertex[] faceVerts = { face.v1, face.v2, face.v3 }; for (int j = 0; j < faceVerts.Length; j++) { if (vertices.Contains(faceVerts[j])) { indices.Add(vertices.IndexOf(faceVerts[j])); } else { indices.Add(vertices.Count); vertices.Add(faceVerts[j]); colors.Add(faceVerts[j].getColor()); } } } } }
/** * Classifies the face based on the ray trace technique * * @param object object3d used to compute the face status */ public void rayTraceClassify(Object3D obj) { //creating a ray starting starting at the face baricenter going to the normal direction Point3d p0 = new Point3d(); p0.x = (v1.x + v2.x + v3.x) / 3d; p0.y = (v1.y + v2.y + v3.y) / 3d; p0.z = (v1.z + v2.z + v3.z) / 3d; Line ray = new Line(getNormal(), p0); bool success; double dotProduct, distance; Point3d intersectionPoint; Face closestFace = null; double closestDistance; do { success = true; closestDistance = Double.MaxValue; //for each face from the other solid... for (int i = 0; i < obj.getNumFaces(); i++) { Face face = obj.getFace(i); dotProduct = face.getNormal().dot(ray.getDirection()); intersectionPoint = ray.computePlaneIntersection(face.getNormal(), face.v1.getPosition()); //if ray intersects the plane... if (intersectionPoint != null) { distance = ray.computePointToPointDistance(intersectionPoint); //if ray lies in plane... if (Math.Abs(distance) < TOL && Math.Abs(dotProduct) < TOL) { //disturb the ray in order to not lie into another plane ray.perturbDirection(); success = false; break; } //if ray starts in plane... if (Math.Abs(distance) < TOL && Math.Abs(dotProduct) > TOL) { //if ray intersects the face... if (face.hasPoint(intersectionPoint)) { //faces coincide closestFace = face; closestDistance = 0; break; } } //if ray intersects plane... else if (Math.Abs(dotProduct) > TOL && distance > TOL) { if (distance < closestDistance) { //if ray intersects the face; if (face.hasPoint(intersectionPoint)) { //this face is the closest face untill now closestDistance = distance; closestFace = face; } } } } } } while(success == false); //none face found: outside face if (closestFace == null) { status = OUTSIDE; } //face found: test dot product else { dotProduct = closestFace.getNormal().dot(ray.getDirection()); //distance = 0: coplanar faces if (Math.Abs(closestDistance) < TOL) { if (dotProduct > TOL) { status = SAME; } else if (dotProduct < -TOL) { status = OPPOSITE; } } //dot product > 0 (same direction): inside face else if (dotProduct > TOL) { status = INSIDE; } //dot product < 0 (opposite direction): outside face else if (dotProduct < -TOL) { status = OUTSIDE; } } }
/// <summary> /// Classifies the face based on the ray trace technique /// </summary> /// <param name="obj">object3d used to compute the face status</param> public void RayTraceClassify(Object3D obj) { //creating a ray starting at the face baricenter going to the normal direction Line ray = new Line(GetNormal(), center); bool success; double distance; Vector3 intersectionPoint; Face closestFace = null; double closestDistance; do { success = true; closestDistance = Double.MaxValue; //for each face from the other solid... for (int faceIndex = 0; faceIndex < obj.GetNumFaces(); faceIndex++) { Face face = obj.GetFace(faceIndex); intersectionPoint = ray.ComputePlaneIntersection(face.GetPlane()); //if ray intersects the plane... if (intersectionPoint.x != double.PositiveInfinity) { double dotProduct = Vector3.Dot(face.GetNormal(), ray.Direction); distance = ray.ComputePointToPointDistance(intersectionPoint); //if ray lies in plane... if (Math.Abs(distance) < EqualityTolerance && Math.Abs(dotProduct) < EqualityTolerance) { //disturb the ray in order to not lie into another plane ray.PerturbDirection(); success = false; break; } //if ray starts in plane... if (Math.Abs(distance) < EqualityTolerance && Math.Abs(dotProduct) > EqualityTolerance) { //if ray intersects the face... if (face.ContainsPoint(intersectionPoint)) { //faces coincide closestFace = face; closestDistance = 0; break; } } //if ray intersects plane... else if (Math.Abs(dotProduct) > EqualityTolerance && distance > EqualityTolerance) { if (distance < closestDistance) { //if ray intersects the face; if (face.ContainsPoint(intersectionPoint)) { //this face is the closest face until now closestDistance = distance; closestFace = face; } } } } } } while (success == false); if (closestFace == null) { //none face found: outside face status = Status.OUTSIDE; } else //face found: test dot product { double dotProduct = Vector3.Dot(closestFace.GetNormal(), ray.Direction); //distance = 0: coplanar faces if (Math.Abs(closestDistance) < EqualityTolerance) { if (dotProduct > EqualityTolerance) { status = Status.SAME; } else if (dotProduct < -EqualityTolerance) { status = Status.OPPOSITE; } } else if (dotProduct > EqualityTolerance) { //dot product > 0 (same direction): inside face status = Status.INSIDE; } else if (dotProduct < -EqualityTolerance) { //dot product < 0 (opposite direction): outside face status = Status.OUTSIDE; } } }