public bool IsValid(decimal epsilon = 0.5m) { return(!GetCoplanarFaces().Any() && // Check coplanar faces !GetBackwardsFaces(epsilon).Any() && // Check faces are pointing outwards !Faces.Any(x => x.GetNonPlanarVertices(epsilon).Any()) && // Check face vertices are all on the plane Faces.All(x => x.IsConvex())); // Check all faces are convex }
public bool IsRectangularPrism() { return(Faces.Count == 6 && Edges.Count == 12 && Vertices.Count == 8 && Faces.All(face => face.IsRectangle())); }
/// <summary> /// Determines whether the mesh is a valid triangle mesh. /// </summary> /// <returns> /// <see langword="true"/> if the mesh is a valid triangle mesh; otherwise, /// <see langword="false"/>. /// </returns> /// <remarks> /// <para> /// This method checks if the mesh consists only of triangle faces without edges or vertices /// that are not connected to a face. /// </para> /// <para> /// This method does not check whether the mesh <see cref="IsValid()"/>. /// </para> /// </remarks> public bool IsTriangleMesh() { if (Vertices.Count > 0 && Faces.Count == 0) { return(false); } return(Faces.All(f => f.Holes == null) && // No holes allowed. Edges.All(e => e.Face == null || e.Next != null && // Face boundary consists of exactly 3 edges. e.Next.Next == e.Previous && e.Next != e.Previous)); }
public override void Transform(Transformations.IUnitTransformation transform, TransformFlags flags) { Faces.ForEach(f => f.Transform(transform, flags)); // Handle flip transforms / negative scales var origin = GetOrigin(); if (Faces.All(x => x.Plane.OnPlane(origin) >= 0)) { // All planes are facing inwards - flip them all Faces.ForEach(x => x.Flip()); } base.Transform(transform, flags); }
// TODO: ORDER the resulting vertices (clockwise)! private void InitializeVertices() { if (Faces.All(face => face.Vertices.Count > 0)) { return; } foreach (var face in Faces) { face.Vertices.Clear(); } // TODO: This is probably O(n^3) or so! // Would it be more efficient to, for each face, use all other faces to cut it down to a polygon? // That would be O(n^2) I think?? for (int i = 0; i < Faces.Count; i++) { for (int j = i + 1; j < Faces.Count; j++) { for (int k = j + 1; k < Faces.Count; k++) { var face1 = Faces[i]; var face2 = Faces[j]; var face3 = Faces[k]; if (Plane.IntersectionPoint(face1.Plane, face2.Plane, face3.Plane) is Vector3D point && this.Contains(point, epsilon: 1f)) // TODO: Figure out what a reasonable epsilon is, and whether this can cause any problems... (a 0 epsilon will sometimes omit vertices!) { // TODO: Look into ORDERING these vertices (CCW or CW) -- it matters for some use-cases! if (!face1.Vertices.Contains(point)) { face1.Vertices.Add(point); } if (!face2.Vertices.Contains(point)) { face2.Vertices.Add(point); } if (!face3.Vertices.Contains(point)) { face3.Vertices.Add(point); } } } } } }
/// <summary> /// Determines whether this mesh is a closed mesh. /// </summary> /// <returns> /// <see langword="true"/> if this instance is a closed mesh; otherwise, <see langword="false"/>. /// </returns> /// <remarks> /// <para> /// In a closed mesh all edges are connected to 2 faces. For example: The mesh of a sphere is a /// closed mesh. An object with holes, like a pullover, is not closed. A flat object, like a /// curtain, is closed if both sides are modeled with faces. It is not closed if only a one side /// is modeled with faces. (A single vertex is considered as closed. A single edge-pair is also /// considered as closed.) /// </para> /// <para> /// This method does not check whether the mesh <see cref="IsValid()"/>. /// </para> /// </remarks> public bool IsClosed() { // Empty mesh is not closed. if (Vertex == null) { return(false); } // A single edge-pair is closed. if (Faces.Count == 0 && Edges.Count <= 2) { return(true); } return(Faces.All(f => f.Holes == null) && // No holes allowed. Edges.All(e => e.Face != null && // Faces on both sides of the edge. e.Twin != null && e.Twin.Face != null)); }
public override void Transform(Transformations.IUnitTransformation transform, TransformFlags flags) { Coordinate newStart = transform.Transform(BoundingBox.Start); Coordinate newEnd = transform.Transform(BoundingBox.End); if ((newStart - newEnd).VectorMagnitude() > 1000000m) { return; } Faces.ForEach(f => f.Transform(transform, flags)); // Handle flip transforms / negative scales var origin = GetOrigin(); if (Faces.All(x => x.Plane.OnPlane(origin) >= 0)) { // All planes are facing inwards - flip them all Faces.ForEach(x => x.Flip()); } base.Transform(transform, flags); }
// TODO: Add error messages to IsXxx method, so that users see where the problem is. /// <summary> /// Gets a value indicating whether the tags of all components in the mesh are equal to the /// given tag value. /// </summary> /// <param name="tag">The reference tag value.</param> /// <returns> /// <see langword="true"/> if the tags of all components are equal to <paramref name="tag"/>; /// otherwise, <see langword="false"/>. /// </returns> public bool AreTagsEqualTo(int tag) { return(Vertices.All(vertex => vertex.Tag == tag) && Edges.All(edge => edge.Tag == tag) && Faces.All(face => face.Tag == tag)); }