diff --git a/Scripts/Core/CSG/Edge.cs b/Scripts/Core/CSG/Edge.cs index a14c989d..2abe5680 100644 --- a/Scripts/Core/CSG/Edge.cs +++ b/Scripts/Core/CSG/Edge.cs @@ -1,17 +1,36 @@ #if UNITY_EDITOR || RUNTIME_CSG + using System; using UnityEngine; namespace Sabresaurus.SabreCSG { + /// + /// An edge describes a connection between two vertices (see ). + /// public class Edge { + /// + /// Since floating-point math is imprecise we use a smaller value of 0.00001 (1e-5f) to check + /// for equality of two floats instead of absolute zero. + /// public const float EPSILON = 1e-5f; - - Vertex vertex1; - Vertex vertex2; - public Vertex Vertex1 + /// + /// The first of the . + /// + private Vertex vertex1; + + /// + /// The last of the . + /// + private Vertex vertex2; + + /// + /// The first of the . + /// + /// The first vertex of the edge. + public Vertex Vertex1 { get { @@ -19,7 +38,11 @@ public Vertex Vertex1 } } - public Vertex Vertex2 + /// + /// The last of the . + /// + /// The last vertex of the edge. + public Vertex Vertex2 { get { @@ -27,149 +50,205 @@ public Vertex Vertex2 } } - public Vector3 GetCenterPoint() - { - return (vertex1.Position + vertex2.Position) * 0.5f; - } + /// + /// Gets the center point between the vertex positions of the edge. + /// + /// The center point of the edge. + public Vector3 CenterPoint + { + get + { + return (vertex1.Position + vertex2.Position) * 0.5f; + } + } - // TODO: Should track entire Vertex here? May be necessary for edge collapse where positions align but UV's don't. Currently unsure if that will be a problem. - public Edge(Vertex vertex1, Vertex vertex2) + /// + /// Initializes a new instance of the class. + /// + /// The first vertex of the edge. + /// The last vertex of the edge. + /// + /// Thrown when or is null. + /// + public Edge(Vertex vertex1, Vertex vertex2) { + if (vertex1 == null) throw new ArgumentNullException("vertex1"); + if (vertex2 == null) throw new ArgumentNullException("vertex2"); + this.vertex1 = vertex1; this.vertex2 = vertex2; } - public bool Matches(Edge otherEdge) + /// + /// Determines whether this edge matches the specified edge. + /// + /// Floating point inaccuracies are taken into account (see ). + /// + /// + /// The other edge to compare this edge to. + /// true if this edge matches the other edge; otherwise, false. + /// + /// Thrown when is null. + /// + public bool Matches(Edge other) { - if (vertex1.Position.EqualsWithEpsilon(otherEdge.vertex1.Position) - && vertex2.Position.EqualsWithEpsilon(otherEdge.Vertex2.Position)) - { + if (other == null) throw new ArgumentNullException("other"); + + // check whether we approximately match the other edge: + if (vertex1.Position.EqualsWithEpsilon(other.vertex1.Position) + && vertex2.Position.EqualsWithEpsilon(other.Vertex2.Position)) return true; - } // Check if the edge is the other way around - else if (vertex1.Position.EqualsWithEpsilon(otherEdge.vertex2.Position) - && vertex2.Position.EqualsWithEpsilon(otherEdge.Vertex1.Position)) - { + + // even if the vertices are swapped it would yield the same edge: + if (vertex1.Position.EqualsWithEpsilon(other.vertex2.Position) + && vertex2.Position.EqualsWithEpsilon(other.Vertex1.Position)) return true; - } - else - { - return false; - } + + // the edge can be considered different. + return false; } - public bool Intersects(Edge otherEdge) - { - Vector3 vector1 = vertex2.Position - vertex1.Position; - Vector3 vector2 = otherEdge.Vertex2.Position - otherEdge.Vertex1.Position; + /// + /// Determines whether the other edge is parallel to this edge. Two edges are considered + /// parallel if they face the same direction, do not intersect or touch each other at any point. + /// Floating point inaccuracies are taken into account (see ). + /// + /// The other edge to check for parallelity. + /// true if both edges are parallel; otherwise, false. + /// + /// Thrown when is null. + /// + public bool Parallel(Edge other) + { + if (other == null) throw new ArgumentNullException("other"); + + Vector3 direction1 = vertex2.Position - vertex1.Position; + Vector3 direction2 = other.Vertex2.Position - other.Vertex1.Position; - float dot = (Vector3.Dot(vector1.normalized, vector2.normalized)); + float dot = Vector3.Dot(direction1.normalized, direction2.normalized); - bool parallel = Mathf.Abs(dot) > 1 - EPSILON; + return Mathf.Abs(dot) > 1 - EPSILON; + } - // Edges not parallel, they can't be collinear - if (!parallel) + /// + /// Does the other edge intersect with this edge? + /// + /// Floating point inaccuracies are taken into account (see ). + /// + /// + /// The other edge to check for intersection. + /// true if both edges intersect; otherwise, false. + /// + /// Thrown when is null. + /// + public bool Intersects(Edge other) + { + if (other == null) throw new ArgumentNullException("other"); + + // early out: if the edges aren't parallel to each other, they can't be collinear. + if (!Parallel(other)) return false; - // TODO: Hacky way of finding out if they are on the same line because we know two points must be the same - bool matchedPoint = false; + // delta from the matched point on this edge to the other point on this edge. + Vector3 delta1 = Vector3.zero; + // delta from the matched point on other edge to the other point on other edge. + Vector3 delta2 = Vector3.zero; - Vector3 delta1 = Vector3.zero; // Delta from the matched point on this edge to the other point on this edge - Vector3 delta2 = Vector3.zero; // Delta from the matched point on otherEdge to the other point on otherEdge + // TODO: hacky way of finding out if they are on the same line because we know + // two points must be the same once CSG geometry has been built. This should + // be able to detect any intersection of edges. + bool matchedPoint = false; - if (vertex1.Position.EqualsWithEpsilon(otherEdge.Vertex1.Position)) - { - matchedPoint = true; + if (vertex1.Position.EqualsWithEpsilon(other.Vertex1.Position)) + { + matchedPoint = true; delta1 = vertex2.Position - vertex1.Position; - delta2 = otherEdge.Vertex2.Position - otherEdge.Vertex1.Position; + delta2 = other.Vertex2.Position - other.Vertex1.Position; } - else if (vertex2.Position.EqualsWithEpsilon(otherEdge.Vertex2.Position)) - { - matchedPoint = true; + else if (vertex2.Position.EqualsWithEpsilon(other.Vertex2.Position)) + { + matchedPoint = true; delta1 = vertex1.Position - vertex2.Position; - delta2 = otherEdge.Vertex1.Position - otherEdge.Vertex2.Position; + delta2 = other.Vertex1.Position - other.Vertex2.Position; } - else if (vertex1.Position.EqualsWithEpsilon(otherEdge.Vertex2.Position)) - { - matchedPoint = true; + else if (vertex1.Position.EqualsWithEpsilon(other.Vertex2.Position)) + { + matchedPoint = true; delta1 = vertex2.Position - vertex1.Position; - delta2 = otherEdge.Vertex1.Position - otherEdge.Vertex2.Position; + delta2 = other.Vertex1.Position - other.Vertex2.Position; } - else if (vertex2.Position.EqualsWithEpsilon(otherEdge.Vertex1.Position)) - { - matchedPoint = true; + else if (vertex2.Position.EqualsWithEpsilon(other.Vertex1.Position)) + { + matchedPoint = true; delta1 = vertex1.Position - vertex2.Position; - delta2 = otherEdge.Vertex2.Position - otherEdge.Vertex1.Position; + delta2 = other.Vertex2.Position - other.Vertex1.Position; } - // No points matched, assume not collinear + // if no points matched, assume it's not collinear: if (!matchedPoint) return false; - // If the two edges actually share a portion then the vectors on the edges from the matched points will be in opposite directions + // if the two edges actually share a portion then the vectors on the edges + // from the matched points will be in opposite directions. bool actuallySharePortion = (Vector3.Dot(delta1, delta2) > 0); - return actuallySharePortion; - } + return actuallySharePortion; + } /// - /// Is this edge collinear with the other edge? + /// Is this edge collinear with the other edge? Two edges are considered collinear if they + /// lie on a single straight line through space (gaps between them make no difference). /// - /// Other edge. - public bool Collinear(Edge otherEdge) + /// Other edge that may be collinear. + /// true if both edges are collinear; otherwise, false. + /// + /// Thrown when is null. + /// + public bool Collinear(Edge other) { - Vector3 vector1 = vertex2.Position - vertex1.Position; - Vector3 vector2 = otherEdge.Vertex2.Position - otherEdge.Vertex1.Position; - - float dot = (Vector3.Dot(vector1.normalized, vector2.normalized)); - - //bool parallel = Mathf.Abs(dot) > 1-Plane.EPSILON; - bool parallel = dot > 1 - EPSILON; + if (other == null) throw new ArgumentNullException("other"); - // TODO: Hacky way of finding out if they are on the same line because we know two points must be the same - bool matchedPoint = false; - - if (vertex1.Position.EqualsWithEpsilon(otherEdge.Vertex1.Position)) - { - matchedPoint = true; - } - else if (vertex2.Position.EqualsWithEpsilon(otherEdge.Vertex2.Position)) - { - matchedPoint = true; - } - else if (vertex1.Position.EqualsWithEpsilon(otherEdge.Vertex2.Position)) - { - matchedPoint = true; - } - else if (vertex2.Position.EqualsWithEpsilon(otherEdge.Vertex1.Position)) - { - matchedPoint = true; - } - return parallel && matchedPoint; + return EdgeUtility.EdgeMatches(this, other); } - public override string ToString () - { - return string.Format (string.Format("[Edge] V1: {0} V2: {1}", vertex1.Position, vertex2.Position)); - } + /// + /// Gets the normalized interpolant between the two vertices of this edge where it intersects + /// with the supplied . + /// + /// The plane that intersects with the edge. + /// The normalized interpolant between the edge points where the plane intersects. + public float GetPlaneIntersectionInterpolant(Plane plane) + { + return GetPlaneIntersectionInterpolant(plane, vertex1.Position, vertex2.Position); + } - #region Static Methods + /// + /// Returns a that represents this . + /// + /// A that represents this . + public override string ToString() + { + return string.Format(string.Format("[Edge] V1: {0} V2: {1}", vertex1.Position, vertex2.Position)); + } /// - /// Returns the normalized interpolant between point1 and point2 where the edge they represent intersects with the - /// supplied plane. + /// Gets the normalized interpolant between and where the edge they + /// represent intersects with the supplied . /// - public static float IntersectsPlane(UnityEngine.Plane plane, Vector3 point1, Vector3 point2) + /// The plane that intersects with the edge. + /// The first point of the edge. + /// The last point of the edge. + /// The normalized interpolant between the edge points where the plane intersects. + public static float GetPlaneIntersectionInterpolant(Plane plane, Vector3 point1, Vector3 point2) { - // TODO: The plane might need flipping here float interpolant = (-plane.normal.x * point1.x - plane.normal.y * point1.y - plane.normal.z * point1.z - plane.distance) / (-plane.normal.x * (point1.x - point2.x) - plane.normal.y * (point1.y - point2.y) - plane.normal.z * (point1.z - point2.z)); -// DISABLED: Should find a way of making this work with the new Assert support in Unity -// DebugHelper.Assert((interpolant >= 0 && interpolant <= 1), "Edge Interpolant outside (0,1) range"); - return interpolant; } - #endregion } } + #endif \ No newline at end of file diff --git a/Scripts/Core/CSG/Polygon.cs b/Scripts/Core/CSG/Polygon.cs index 887d851d..9d7a3da5 100644 --- a/Scripts/Core/CSG/Polygon.cs +++ b/Scripts/Core/CSG/Polygon.cs @@ -498,7 +498,7 @@ public static bool SplitPolygon(Polygon polygon, out Polygon frontPolygon, out P } else if (previousRelation == PointPlaneRelation.Behind && currentRelation == PointPlaneRelation.InFront) { - float interpolant = Edge.IntersectsPlane(clipPlane, previousVertex.Position, currentVertex.Position); + float interpolant = Edge.GetPlaneIntersectionInterpolant(clipPlane, previousVertex.Position, currentVertex.Position); Vertex intersection = Vertex.Lerp(previousVertex, currentVertex, interpolant); // Front add intersection, add current @@ -513,7 +513,7 @@ public static bool SplitPolygon(Polygon polygon, out Polygon frontPolygon, out P else if (previousRelation == PointPlaneRelation.InFront && currentRelation == PointPlaneRelation.Behind) { // Reverse order here so that clipping remains consistent for either CW or CCW testing - float interpolant = Edge.IntersectsPlane(clipPlane, currentVertex.Position, previousVertex.Position); + float interpolant = Edge.GetPlaneIntersectionInterpolant(clipPlane, currentVertex.Position, previousVertex.Position); Vertex intersection = Vertex.Lerp(currentVertex, previousVertex, interpolant); // Front add intersection @@ -618,14 +618,14 @@ public static bool PlanePolygonIntersection(Polygon polygon, out Vector3 positio } else if (previousRelation == PointPlaneRelation.Behind && currentRelation == PointPlaneRelation.InFront) { - float interpolant = Edge.IntersectsPlane(testPlane, previousVertex.Position, currentVertex.Position); + float interpolant = Edge.GetPlaneIntersectionInterpolant(testPlane, previousVertex.Position, currentVertex.Position); position2 = Vector3.Lerp(previousVertex.Position, currentVertex.Position, interpolant); position2Set = true; } else if (previousRelation == PointPlaneRelation.InFront && currentRelation == PointPlaneRelation.Behind) { // Reverse order here so that clipping remains consistent for either CW or CCW testing - float interpolant = Edge.IntersectsPlane(testPlane, currentVertex.Position, previousVertex.Position); + float interpolant = Edge.GetPlaneIntersectionInterpolant(testPlane, currentVertex.Position, previousVertex.Position); position1 = Vector3.Lerp(currentVertex.Position, previousVertex.Position, interpolant); position1Set = true; } diff --git a/Scripts/Tools/Utilities/EdgeUtility.cs b/Scripts/Tools/Utilities/EdgeUtility.cs index cbc6070a..f069a63e 100644 --- a/Scripts/Tools/Utilities/EdgeUtility.cs +++ b/Scripts/Tools/Utilities/EdgeUtility.cs @@ -98,8 +98,8 @@ public static void SplitPolygonsByEdges(Polygon[] polygons, List sourceEdg Edge edge2 = edgesOnPolygon[1]; // First split the shared polygon - Vector3 edge1Center = edge1.GetCenterPoint(); - Vector3 edge2Center = edge2.GetCenterPoint(); + Vector3 edge1Center = edge1.CenterPoint; + Vector3 edge2Center = edge2.CenterPoint; Vector3 thirdPoint = edge1Center + polygon.Plane.normal;