Skip to content
This repository has been archived by the owner on Feb 11, 2024. It is now read-only.

Commit

Permalink
Merge pull request #178 from Henry00IS/CleanupEdge
Browse files Browse the repository at this point in the history
Cleanup and documentation: Scripts/Core/CSG/Edge.cs
  • Loading branch information
Henry00IS authored Nov 4, 2018
2 parents c60c310 + 611962b commit 15f1a00
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 105 deletions.
277 changes: 178 additions & 99 deletions Scripts/Core/CSG/Edge.cs
Original file line number Diff line number Diff line change
@@ -1,175 +1,254 @@
#if UNITY_EDITOR || RUNTIME_CSG

using System;
using UnityEngine;

namespace Sabresaurus.SabreCSG
{
/// <summary>
/// An edge describes a connection between two vertices (see <see cref="Vertex"/>).
/// </summary>
public class Edge
{
/// <summary>
/// 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.
/// </summary>
public const float EPSILON = 1e-5f;

Vertex vertex1;
Vertex vertex2;

public Vertex Vertex1
/// <summary>
/// The first <see cref="Vertex"/> of the <see cref="Edge"/>.
/// </summary>
private Vertex vertex1;

/// <summary>
/// The last <see cref="Vertex"/> of the <see cref="Edge"/>.
/// </summary>
private Vertex vertex2;

/// <summary>
/// The first <see cref="Vertex"/> of the <see cref="Edge"/>.
/// </summary>
/// <value>The first vertex of the edge.</value>
public Vertex Vertex1
{
get
{
return this.vertex1;
}
}

public Vertex Vertex2
/// <summary>
/// The last <see cref="Vertex"/> of the <see cref="Edge"/>.
/// </summary>
/// <value>The last vertex of the edge.</value>
public Vertex Vertex2
{
get
{
return this.vertex2;
}
}

public Vector3 GetCenterPoint()
{
return (vertex1.Position + vertex2.Position) * 0.5f;
}
/// <summary>
/// Gets the center point between the vertex positions of the edge.
/// </summary>
/// <returns>The center point of the edge.</returns>
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)
/// <summary>
/// Initializes a new instance of the <see cref="Edge"/> class.
/// </summary>
/// <param name="vertex1">The first vertex of the edge.</param>
/// <param name="vertex2">The last vertex of the edge.</param>
/// <exception cref="System.ArgumentNullException">
/// Thrown when <paramref name="vertex1"/> or <paramref name="vertex2"/> is null.
/// </exception>
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)
/// <summary>
/// Determines whether this edge matches the specified edge.
/// <para>
/// Floating point inaccuracies are taken into account (see <see
/// cref="Extensions.EqualsWithEpsilon(Vector3, Vector3)"/>).
/// </para>
/// </summary>
/// <param name="other">The other edge to compare this edge to.</param>
/// <returns><c>true</c> if this edge matches the other edge; otherwise, <c>false</c>.</returns>
/// <exception cref="System.ArgumentNullException">
/// Thrown when <paramref name="other"/> is null.
/// </exception>
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;
/// <summary>
/// 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.
/// <para>Floating point inaccuracies are taken into account (see <see cref="EPSILON"/>).</para>
/// </summary>
/// <param name="other">The other edge to check for parallelity.</param>
/// <returns><c>true</c> if both edges are parallel; otherwise, <c>false</c>.</returns>
/// <exception cref="System.ArgumentNullException">
/// Thrown when <paramref name="other"/> is null.
/// </exception>
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)
/// <summary>
/// Does the other edge intersect with this edge?
/// <para>
/// Floating point inaccuracies are taken into account (see <see
/// cref="Extensions.EqualsWithEpsilon(Vector3, Vector3)"/>).
/// </para>
/// </summary>
/// <param name="other">The other edge to check for intersection.</param>
/// <returns><c>true</c> if both edges intersect; otherwise, <c>false</c>.</returns>
/// <exception cref="System.ArgumentNullException">
/// Thrown when <paramref name="other"/> is null.
/// </exception>
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;
}

/// <summary>
/// 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).
/// </summary>
/// <param name="otherEdge">Other edge.</param>
public bool Collinear(Edge otherEdge)
/// <param name="other">Other edge that may be collinear.</param>
/// <returns><c>true</c> if both edges are collinear; otherwise, <c>false</c>.</returns>
/// <exception cref="System.ArgumentNullException">
/// Thrown when <paramref name="other"/> is null.
/// </exception>
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));
}
/// <summary>
/// Gets the normalized interpolant between the two vertices of this edge where it intersects
/// with the supplied <paramref name="plane"/>.
/// </summary>
/// <param name="plane">The plane that intersects with the edge.</param>
/// <returns>The normalized interpolant between the edge points where the plane intersects.</returns>
public float GetPlaneIntersectionInterpolant(Plane plane)
{
return GetPlaneIntersectionInterpolant(plane, vertex1.Position, vertex2.Position);
}

#region Static Methods
/// <summary>
/// Returns a <see cref="string"/> that represents this <see cref="Edge"/>.
/// </summary>
/// <returns>A <see cref="string"/> that represents this <see cref="Edge"/>.</returns>
public override string ToString()
{
return string.Format(string.Format("[Edge] V1: {0} V2: {1}", vertex1.Position, vertex2.Position));
}

/// <summary>
/// Returns the normalized interpolant between point1 and point2 where the edge they represent intersects with the
/// supplied plane.
/// Gets the normalized interpolant between <paramref name="point1"/> and <paramref name="point2"/> where the edge they
/// represent intersects with the supplied <paramref name="plane"/>.
/// </summary>
public static float IntersectsPlane(UnityEngine.Plane plane, Vector3 point1, Vector3 point2)
/// <param name="plane">The plane that intersects with the edge.</param>
/// <param name="point1">The first point of the edge.</param>
/// <param name="point2">The last point of the edge.</param>
/// <returns>The normalized interpolant between the edge points where the plane intersects.</returns>
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
8 changes: 4 additions & 4 deletions Scripts/Core/CSG/Polygon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down
Loading

0 comments on commit 15f1a00

Please sign in to comment.