//----------------------------------CONSTRUCTORS---------------------------------// /** * Constructor for a line. The line created is the intersection between two planes * * @param face1 face representing one of the planes * @param face2 face representing one of the planes */ public Line(Face face1, Face face2) { Vector3d normalFace1 = face1.getNormal(); Vector3d normalFace2 = face2.getNormal(); //direction: cross product of the faces normals direction = new Vector3d(); direction.cross(normalFace1, normalFace2); //if direction lenght is not zero (the planes aren't parallel )... if (!(direction.length() < TOL)) { //getting a line point, zero is set to a coordinate whose direction //component isn't zero (line intersecting its origin plan) point = new Point3d(); double d1 = -(normalFace1.x * face1.v1.x + normalFace1.y * face1.v1.y + normalFace1.z * face1.v1.z); double d2 = -(normalFace2.x * face2.v1.x + normalFace2.y * face2.v1.y + normalFace2.z * face2.v1.z); if (Math.Abs(direction.x) > TOL) { point.x = 0; point.y = (d2 * normalFace1.z - d1 * normalFace2.z) / direction.x; point.z = (d1 * normalFace2.y - d2 * normalFace1.y) / direction.x; } else if (Math.Abs(direction.y) > TOL) { point.x = (d1 * normalFace2.z - d2 * normalFace1.z) / direction.y; point.y = 0; point.z = (d2 * normalFace1.x - d1 * normalFace2.x) / direction.y; } else { point.x = (d2 * normalFace1.y - d1 * normalFace2.y) / direction.z; point.y = (d1 * normalFace2.x - d2 * normalFace1.x) / direction.z; point.z = 0; } } direction.normalize(); }
//---------------------------------CONSTRUCTORS---------------------------------// /** * Constructs a Segment based on elements obtained from the two planes relations * * @param line resulting from the two planes intersection * @param face face that intersects with the plane * @param sign1 position of the face vertex1 relative to the plane (-1 behind, 1 front, 0 on) * @param sign2 position of the face vertex1 relative to the plane (-1 behind, 1 front, 0 on) * @param sign3 position of the face vertex1 relative to the plane (-1 behind, 1 front, 0 on) */ public Segment(Line line, Face face, int sign1, int sign2, int sign3) { this.line = line; index = 0; //VERTEX is an end if (sign1 == 0) { setVertex(face.v1); //other vertices on the same side - VERTEX-VERTEX VERTEX if (sign2 == sign3) { setVertex(face.v1); } } //VERTEX is an end if (sign2 == 0) { setVertex(face.v2); //other vertices on the same side - VERTEX-VERTEX VERTEX if (sign1 == sign3) { setVertex(face.v2); } } //VERTEX is an end if (sign3 == 0) { setVertex(face.v3); //other vertices on the same side - VERTEX-VERTEX VERTEX if (sign1 == sign2) { setVertex(face.v3); } } //There are undefined ends - one or more edges cut the planes intersection line if (getNumEndsSet() != 2) { //EDGE is an end if ((sign1 == 1 && sign2 == -1) || (sign1 == -1 && sign2 == 1)) { setEdge(face.v1, face.v2); } //EDGE is an end if ((sign2 == 1 && sign3 == -1) || (sign2 == -1 && sign3 == 1)) { setEdge(face.v2, face.v3); } //EDGE is an end if ((sign3 == 1 && sign1 == -1) || (sign3 == -1 && sign1 == 1)) { setEdge(face.v3, face.v1); } } }
/** * Checks if a face is equal to another. To be equal, they have to have equal * vertices in the same order * * @param anObject the other face to be tested * @return true if they are equal, false otherwise. */ public bool equals(Face face) { bool cond1 = v1.equals(face.v1) && v2.equals(face.v2) && v3.equals(face.v3); bool cond2 = v1.equals(face.v2) && v2.equals(face.v3) && v3.equals(face.v1); bool cond3 = v1.equals(face.v3) && v2.equals(face.v1) && v3.equals(face.v2); return cond1 || cond2 || cond3; }
//-----------------------------------OVERRIDES----------------------------------// /** * Clones the face object * * @return cloned face object */ public Face Clone() { Face clone = new Face(); clone.v1 = v1.Clone(); clone.v2 = v2.Clone(); clone.v3 = v3.Clone(); clone.status = status; return clone; }
/** * Split an individual face * * @param facePos face position on the array of faces * @param segment1 segment representing the intersection of the face with the plane * of another face * @return segment2 segment representing the intersection of other face with the * plane of the current face plane */ private void splitFace(int facePos, Segment segment1, Segment segment2) { Vertex startPosVertex, endPosVertex; Point3d startPos, endPos; int startType, endType, middleType; double startDist, endDist; Face face = getFace(facePos); Vertex startVertex = segment1.getStartVertex(); Vertex endVertex = segment1.getEndVertex(); //starting point: deeper starting point if (segment2.getStartDistance() > segment1.getStartDistance() + TOL) { startDist = segment2.getStartDistance(); startType = segment1.getIntermediateType(); startPos = segment2.getStartPosition(); } else { startDist = segment1.getStartDistance(); startType = segment1.getStartType(); startPos = segment1.getStartPosition(); } //ending point: deepest ending point if (segment2.getEndDistance() < segment1.getEndDistance() - TOL) { endDist = segment2.getEndDistance(); endType = segment1.getIntermediateType(); endPos = segment2.getEndPosition(); } else { endDist = segment1.getEndDistance(); endType = segment1.getEndType(); endPos = segment1.getEndPosition(); } middleType = segment1.getIntermediateType(); //set vertex to BOUNDARY if it is start type if (startType == Segment.VERTEX) { startVertex.setStatus(Vertex.BOUNDARY); } //set vertex to BOUNDARY if it is end type if (endType == Segment.VERTEX) { endVertex.setStatus(Vertex.BOUNDARY); } //VERTEX-_______-VERTEX if (startType == Segment.VERTEX && endType == Segment.VERTEX) { return; } //______-EDGE-______ else if (middleType == Segment.EDGE) { //gets the edge int splitEdge; if ((startVertex == face.v1 && endVertex == face.v2) || (startVertex == face.v2 && endVertex == face.v1)) { splitEdge = 1; } else if ((startVertex == face.v2 && endVertex == face.v3) || (startVertex == face.v3 && endVertex == face.v2)) { splitEdge = 2; } else { splitEdge = 3; } //VERTEX-EDGE-EDGE if (startType == Segment.VERTEX) { breakFaceInTwo(facePos, endPos, splitEdge); return; } //EDGE-EDGE-VERTEX else if (endType == Segment.VERTEX) { breakFaceInTwo(facePos, startPos, splitEdge); return; } // EDGE-EDGE-EDGE else if (startDist == endDist) { breakFaceInTwo(facePos, endPos, splitEdge); } else { if ((startVertex == face.v1 && endVertex == face.v2) || (startVertex == face.v2 && endVertex == face.v3) || (startVertex == face.v3 && endVertex == face.v1)) { breakFaceInThree(facePos, startPos, endPos, splitEdge); } else { breakFaceInThree(facePos, endPos, startPos, splitEdge); } } return; } //______-FACE-______ //VERTEX-FACE-EDGE else if (startType == Segment.VERTEX && endType == Segment.EDGE) { breakFaceInTwo(facePos, endPos, endVertex); } //EDGE-FACE-VERTEX else if (startType == Segment.EDGE && endType == Segment.VERTEX) { breakFaceInTwo(facePos, startPos, startVertex); } //VERTEX-FACE-FACE else if (startType == Segment.VERTEX && endType == Segment.FACE) { breakFaceInThree(facePos, endPos, startVertex); } //FACE-FACE-VERTEX else if (startType == Segment.FACE && endType == Segment.VERTEX) { breakFaceInThree(facePos, startPos, endVertex); } //EDGE-FACE-EDGE else if (startType == Segment.EDGE && endType == Segment.EDGE) { breakFaceInThree(facePos, startPos, endPos, startVertex, endVertex); } //EDGE-FACE-FACE else if (startType == Segment.EDGE && endType == Segment.FACE) { breakFaceInFour(facePos, startPos, endPos, startVertex); } //FACE-FACE-EDGE else if (startType == Segment.FACE && endType == Segment.EDGE) { breakFaceInFour(facePos, endPos, startPos, endVertex); } //FACE-FACE-FACE else if (startType == Segment.FACE && endType == Segment.FACE) { Vector3d segmentVector = new Vector3d(startPos.x - endPos.x, startPos.y - endPos.y, startPos.z - endPos.z); //if the intersection segment is a point only... if (Math.Abs(segmentVector.x) < TOL && Math.Abs(segmentVector.y) < TOL && Math.Abs(segmentVector.z) < TOL) { breakFaceInThree(facePos, startPos); return; } //gets the vertex more lined with the intersection segment int linedVertex; Point3d linedVertexPos; Vector3d vertexVector = new Vector3d(endPos.x - face.v1.x, endPos.y - face.v1.y, endPos.z - face.v1.z); vertexVector.normalize(); double dot1 = Math.Abs(segmentVector.dot(vertexVector)); vertexVector = new Vector3d(endPos.x - face.v2.x, endPos.y - face.v2.y, endPos.z - face.v2.z); vertexVector.normalize(); double dot2 = Math.Abs(segmentVector.dot(vertexVector)); vertexVector = new Vector3d(endPos.x - face.v3.x, endPos.y - face.v3.y, endPos.z - face.v3.z); vertexVector.normalize(); double dot3 = Math.Abs(segmentVector.dot(vertexVector)); if (dot1 > dot2 && dot1 > dot3) { linedVertex = 1; linedVertexPos = face.v1.getPosition(); } else if (dot2 > dot3 && dot2 > dot1) { linedVertex = 2; linedVertexPos = face.v2.getPosition(); } else { linedVertex = 3; linedVertexPos = face.v3.getPosition(); } // Now find which of the intersection endpoints is nearest to that vertex. if (linedVertexPos.distance(startPos) > linedVertexPos.distance(endPos)) { breakFaceInFive(facePos, startPos, endPos, linedVertex); } else { breakFaceInFive(facePos, endPos, startPos, linedVertex); } } }
/// <summary> /// Split an individual face /// </summary> /// <param name="facePos">face position on the array of faces</param> /// <param name="segment1">segment representing the intersection of the face with the plane</param> /// <param name="segment2">segment representing the intersection of other face with the plane of the current face plane</param> private void SplitFace(int facePos, Segment segment1, Segment segment2) { Vector3 startPos, endPos; int startType, endType, middleType; double startDist, endDist; Face face = GetFace(facePos); Vertex startVertex = segment1.GetStartVertex(); Vertex endVertex = segment1.GetEndVertex(); //starting point: deeper starting point if (segment2.StartDist > segment1.StartDist + EqualityTolerance) { startDist = segment2.StartDist; startType = segment1.GetIntermediateType(); startPos = segment2.GetStartPosition(); } else { startDist = segment1.StartDist; startType = segment1.GetStartType(); startPos = segment1.GetStartPosition(); } //ending point: deepest ending point if (segment2.GetEndDistance() < segment1.GetEndDistance() - EqualityTolerance) { endDist = segment2.GetEndDistance(); endType = segment1.GetIntermediateType(); endPos = segment2.GetEndPosition(); } else { endDist = segment1.GetEndDistance(); endType = segment1.GetEndType(); endPos = segment1.GetEndPosition(); } middleType = segment1.GetIntermediateType(); //set vertex to BOUNDARY if it is start type if (startType == Segment.VERTEX) { startVertex.SetStatus(Status.BOUNDARY); } //set vertex to BOUNDARY if it is end type if (endType == Segment.VERTEX) { endVertex.SetStatus(Status.BOUNDARY); } //VERTEX-_______-VERTEX if (startType == Segment.VERTEX && endType == Segment.VERTEX) { return; } //______-EDGE-______ else if (middleType == Segment.EDGE) { //gets the edge int splitEdge; if ((startVertex == face.v1 && endVertex == face.v2) || (startVertex == face.v2 && endVertex == face.v1)) { splitEdge = 1; } else if ((startVertex == face.v2 && endVertex == face.v3) || (startVertex == face.v3 && endVertex == face.v2)) { splitEdge = 2; } else { splitEdge = 3; } //VERTEX-EDGE-EDGE if (startType == Segment.VERTEX) { BreakFaceInTwo(facePos, endPos, splitEdge); return; } //EDGE-EDGE-VERTEX else if (endType == Segment.VERTEX) { BreakFaceInTwo(facePos, startPos, splitEdge); return; } // EDGE-EDGE-EDGE else if (startDist == endDist) { BreakFaceInTwo(facePos, endPos, splitEdge); } else { if ((startVertex == face.v1 && endVertex == face.v2) || (startVertex == face.v2 && endVertex == face.v3) || (startVertex == face.v3 && endVertex == face.v1)) { BreakFaceInThree(facePos, startPos, endPos, splitEdge); } else { BreakFaceInThree(facePos, endPos, startPos, splitEdge); } } return; } //______-FACE-______ //VERTEX-FACE-EDGE else if (startType == Segment.VERTEX && endType == Segment.EDGE) { BreakFaceInTwo(facePos, endPos, endVertex); } //EDGE-FACE-VERTEX else if (startType == Segment.EDGE && endType == Segment.VERTEX) { BreakFaceInTwo(facePos, startPos, startVertex); } //VERTEX-FACE-FACE else if (startType == Segment.VERTEX && endType == Segment.FACE) { BreakFaceInThree(facePos, endPos, startVertex); } //FACE-FACE-VERTEX else if (startType == Segment.FACE && endType == Segment.VERTEX) { BreakFaceInThree(facePos, startPos, endVertex); } //EDGE-FACE-EDGE else if (startType == Segment.EDGE && endType == Segment.EDGE) { BreakFaceInThree(facePos, startPos, endPos, startVertex, endVertex); } //EDGE-FACE-FACE else if (startType == Segment.EDGE && endType == Segment.FACE) { BreakFaceInFour(facePos, startPos, endPos, startVertex); } //FACE-FACE-EDGE else if (startType == Segment.FACE && endType == Segment.EDGE) { BreakFaceInFour(facePos, endPos, startPos, endVertex); } //FACE-FACE-FACE else if (startType == Segment.FACE && endType == Segment.FACE) { Vector3 segmentVector = new Vector3(startPos.x - endPos.x, startPos.y - endPos.y, startPos.z - endPos.z); //if the intersection segment is a point only... if (Math.Abs(segmentVector.x) < EqualityTolerance && Math.Abs(segmentVector.y) < EqualityTolerance && Math.Abs(segmentVector.z) < EqualityTolerance) { BreakFaceInThree(facePos, startPos); return; } //gets the vertex more lined with the intersection segment int linedVertex; Vector3 linedVertexPos; Vector3 vertexVector = new Vector3(endPos.x - face.v1.Position.x, endPos.y - face.v1.Position.y, endPos.z - face.v1.Position.z); vertexVector.Normalize(); double dot1 = Math.Abs(Vector3.Dot(segmentVector, vertexVector)); vertexVector = new Vector3(endPos.x - face.v2.Position.x, endPos.y - face.v2.Position.y, endPos.z - face.v2.Position.z); vertexVector.Normalize(); double dot2 = Math.Abs(Vector3.Dot(segmentVector, vertexVector)); vertexVector = new Vector3(endPos.x - face.v3.Position.x, endPos.y - face.v3.Position.y, endPos.z - face.v3.Position.z); vertexVector.Normalize(); double dot3 = Math.Abs(Vector3.Dot(segmentVector, vertexVector)); if (dot1 > dot2 && dot1 > dot3) { linedVertex = 1; linedVertexPos = face.v1.GetPosition(); } else if (dot2 > dot3 && dot2 > dot1) { linedVertex = 2; linedVertexPos = face.v2.GetPosition(); } else { linedVertex = 3; linedVertexPos = face.v3.GetPosition(); } // Now find which of the intersection endpoints is nearest to that vertex. if ((linedVertexPos - startPos).Length > (linedVertexPos - endPos).Length) { BreakFaceInFive(facePos, startPos, endPos, linedVertex); } else { BreakFaceInFive(facePos, endPos, startPos, linedVertex); } } }
/// <summary> /// Face breaker for EDGE-EDGE-EDGE /// </summary> /// <param name="faceIndex">face index in the faces array</param> /// <param name="newPos1">new vertex position</param> /// <param name="newPos2">new vertex position</param> /// <param name="splitEdge">edge that will be split</param> private bool BreakFaceInThree(int faceIndex, Vector3 newPos1, Vector3 newPos2, int splitEdge) { // O // - - // - X // - * - // - * **X // -* **** - // X-----------O Vertex vertex1 = AddVertex(newPos1, Status.BOUNDARY); Vertex vertex2 = AddVertex(newPos2, Status.BOUNDARY); if (splitEdge == 1) // vertex 3 { Face face = faces[faceIndex]; bool willMakeExistingFace = (vertex1 == face.v1 && vertex2 == face.v2) || (vertex1 == face.v2 || vertex2 == face.v1); if (!willMakeExistingFace) { faces.RemoveAt(faceIndex); AddFace(face.v1, vertex1, face.v3); AddFace(vertex1, vertex2, face.v3); AddFace(vertex2, face.v2, face.v3); return(true); } } else if (splitEdge == 2) // vertex 1 { Face face = faces[faceIndex]; bool willMakeExistingFace = (vertex1 == face.v2 && vertex2 == face.v3) || (vertex1 == face.v3 || vertex2 == face.v2); if (!willMakeExistingFace) { faces.RemoveAt(faceIndex); AddFace(face.v2, vertex1, face.v1); AddFace(vertex1, vertex2, face.v1); AddFace(vertex2, face.v3, face.v1); return(true); } } else // vertex 2 { Face face = faces[faceIndex]; bool willMakeExistingFace = (vertex1 == face.v1 && vertex2 == face.v3) || (vertex1 == face.v3 || vertex2 == face.v1); if (!willMakeExistingFace) { faces.RemoveAt(faceIndex); AddFace(face.v3, vertex1, face.v2); AddFace(vertex1, vertex2, face.v2); AddFace(vertex2, face.v1, face.v2); return(true); } } if (vertex2 == vertices[vertices.Count - 1]) { vertices.RemoveAt(vertices.Count - 1); } if (vertex1 == vertices[vertices.Count - 1]) { vertices.RemoveAt(vertices.Count - 1); } return(false); }
/** * 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; Vector3d 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.NumFaces; faceIndex++) { Face face = obj.GetFace(faceIndex); intersectionPoint = ray.ComputePlaneIntersection(face.GetPlane()); //if ray intersects the plane... if (intersectionPoint.X != double.PositiveInfinity) { double dotProduct = Vector3d.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 untill 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 = Vector3d.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; } } }
/** * Computes closest distance from a vertex to a plane * * @param vertex vertex used to compute the distance * @param face face representing the plane where it is contained * @return the closest distance from the vertex to the plane */ private double computeDistance(Vertex vertex, Face face) { Vector3d normal = face.getNormal(); double a = normal.x; double b = normal.y; double c = normal.z; double d = -(a * face.v1.x + b * face.v1.y + c * face.v1.z); return a * vertex.x + b * vertex.y + c * vertex.z + d; }
//------------------------------------ADDS----------------------------------------// /** * Method used to add a face properly for internal methods * * @param v1 a face vertex * @param v2 a face vertex * @param v3 a face vertex */ private Face addFace(Vertex v1, Vertex v2, Vertex v3) { if (!(v1.equals(v2) || v1.equals(v3) || v2.equals(v3))) { Face face = new Face(v1, v2, v3); if (face.getArea() > TOL) { faces.Add(face); return face; } else { return null; } } else { return null; } }