// Copyright (c) 2014-2015 Robert Rouhani and other contributors (see CONTRIBUTORS file). // Licensed under the MIT License - https://raw.github.com/Robmaister/SharpNav/master/LICENSE using System; using System.Collections.Generic; using System.Runtime.InteropServices; namespace SharpNav { /// /// A vertex inside a . /// [StructLayout(LayoutKind.Sequential)] public struct PolyVertex : IEquatable { /// /// The X coordinate. /// public int X; /// /// The Y coordinate. /// public int Y; /// /// The Z coordinate. /// public int Z; /// /// Initializes a new instance of the struct. /// /// The X coordinate. /// The Y coordinate. /// The Z coordinate. public PolyVertex(int x, int y, int z) { X = x; Y = y; Z = z; } /// /// Calculates the component-wise minimum of two vertices. /// /// A vertex. /// Another vertex. /// The component-wise minimum of the two vertices. public static PolyVertex ComponentMin(PolyVertex a, PolyVertex b) { PolyVertex v; ComponentMin(ref a, ref b, out v); return v; } /// /// Calculates the component-wise minimum of two vertices. /// /// A vertex. /// Another vertex. /// The component-wise minimum of the two vertices. public static void ComponentMin(ref PolyVertex a, ref PolyVertex b, out PolyVertex result) { result.X = a.X < b.X ? a.X : b.X; result.Y = a.Y < b.Y ? a.Y : b.Y; result.Z = a.Z < b.Z ? a.Z : b.Z; } /// /// Calculates the component-wise maximum of two vertices. /// /// A vertex. /// Another vertex. /// The component-wise maximum of the two vertices. public static PolyVertex ComponentMax(PolyVertex a, PolyVertex b) { PolyVertex v; ComponentMax(ref a, ref b, out v); return v; } /// /// Calculates the component-wise maximum of two vertices. /// /// A vertex. /// Another vertex. /// The component-wise maximum of the two vertices. public static void ComponentMax(ref PolyVertex a, ref PolyVertex b, out PolyVertex result) { result.X = a.X > b.X ? a.X : b.X; result.Y = a.Y > b.Y ? a.Y : b.Y; result.Z = a.Z > b.Z ? a.Z : b.Z; } /// /// Gets the leftness of a triangle formed from 3 contour vertices. /// /// The first vertex. /// The second vertex. /// The third vertex. /// A value indicating the leftness of the triangle. public static bool IsLeft(ref PolyVertex a, ref PolyVertex b, ref PolyVertex c) { int area; Area2D(ref a, ref b, ref c, out area); return area < 0; } /// /// Gets the leftness (left or on) of a triangle formed from 3 contour vertices. /// /// The first vertex. /// The second vertex. /// The third vertex. /// A value indicating whether the triangle is left or on. public static bool IsLeftOn(ref PolyVertex a, ref PolyVertex b, ref PolyVertex c) { int area; Area2D(ref a, ref b, ref c, out area); return area <= 0; } /// /// Compares vertex equality in 2D. /// /// A vertex. /// Another vertex. /// A value indicating whether the X and Z components of both vertices are equal. public static bool Equal2D(ref PolyVertex a, ref PolyVertex b) { return a.X == b.X && a.Z == b.Z; } /// /// True if and only if segments AB and CD intersect, properly or improperly. /// /// Point A of segment AB. /// Point B of segment AB. /// Point C of segment CD. /// Point D of segment CD. /// A value indicating whether segments AB and CD intersect. public static bool Intersect(ref PolyVertex a, ref PolyVertex b, ref PolyVertex c, ref PolyVertex d) { if (IntersectProp(ref a, ref b, ref c, ref d)) return true; else if (IsBetween(ref a, ref b, ref c) || IsBetween(ref a, ref b, ref d) || IsBetween(ref c, ref d, ref a) || IsBetween(ref c, ref d, ref b)) return true; else return false; } /// /// True if and only if segments AB and CD intersect properly. /// /// /// Proper intersection: A point interior to both segments is shared. Properness determined by strict leftness. /// /// Point A of segment AB. /// Point B of segment AB. /// Point C of segment CD. /// Point D of segment CD. /// A value indicating whether segements AB and CD are intersecting properly. public static bool IntersectProp(ref PolyVertex a, ref PolyVertex b, ref PolyVertex c, ref PolyVertex d) { //eliminate improper cases if (IsCollinear(ref a, ref b, ref c) || IsCollinear(ref a, ref b, ref d) || IsCollinear(ref c, ref d, ref a) || IsCollinear(ref c, ref d, ref b)) return false; return (!IsLeft(ref a, ref b, ref c) ^ !IsLeft(ref a, ref b, ref d)) && (!IsLeft(ref c, ref d, ref a) ^ !IsLeft(ref c, ref d, ref b)); } /// /// True if and only if A, B, and C are collinear and point C lies on closed segment AB /// /// Point A of segment AB. /// Point B of segment AB. /// Point C. /// A value indicating whether the three points are collinear with C in the middle. public static bool IsBetween(ref PolyVertex a, ref PolyVertex b, ref PolyVertex c) { if (!IsCollinear(ref a, ref b, ref c)) return false; if (a.X != b.X) return ((a.X <= c.X) && (c.X <= b.X)) || ((a.X >= c.X) && (c.X >= b.X)); else return ((a.Z <= c.Z) && (c.Z <= b.Z)) || ((a.Z >= c.Z) && (c.Z >= b.Z)); } /// /// True if and only if points A, B, and C are collinear. /// /// Point A. /// Point B. /// Point C. /// A value indicating whether the points are collinear. public static bool IsCollinear(ref PolyVertex a, ref PolyVertex b, ref PolyVertex c) { int area; Area2D(ref a, ref b, ref c, out area); return area == 0; } /// /// Gets the 2D area of the triangle ABC. /// /// Point A of triangle ABC. /// Point B of triangle ABC. /// Point C of triangle ABC. /// The 2D area of the triangle. public static void Area2D(ref PolyVertex a, ref PolyVertex b, ref PolyVertex c, out int area) { area = (b.X - a.X) * (c.Z - a.Z) - (c.X - a.X) * (b.Z - a.Z); } /// /// Gets the 2D area of the triangle ABC. /// /// Point A of triangle ABC. /// Point B of triangle ABC. /// Point C of triangle ABC. /// The 2D area of the triangle. public static void Area2D(ref ContourVertex a, ref ContourVertex b, ref ContourVertex c, out int area) { area = (b.X - a.X) * (c.Z - a.Z) - (c.X - a.X) * (b.Z - a.Z); } /// /// Compares two vertices for equality. /// /// A vertex. /// Another vertex. /// A value indicating whether the two vertices are equal. public static bool operator ==(PolyVertex left, PolyVertex right) { return left.Equals(right); } /// /// Compares two vertices for inequality. /// /// A vertex. /// Another vertex. /// A value indicating whether the two vertices are not equal. public static bool operator !=(PolyVertex left, PolyVertex right) { return !(left == right); } /// /// Compares another with this instance for equality. /// /// The other instance. /// A value indicating whether the two vertices are equal. public bool Equals(PolyVertex other) { return X == other.X && Y == other.Y && Z == other.Z; } /// /// Compares an object with this instance for equality. /// /// An object. /// A value indicating whether the object is equal to this instance. public override bool Equals(object obj) { PolyVertex? p = obj as PolyVertex?; if (p.HasValue) return this.Equals(p.Value); return false; } /// /// Gets a hash code unique to the contents of this instance. /// /// A hash code. public override int GetHashCode() { //TODO write a better hashcode return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode(); } /// /// Gets a human-readable version of the vertex. /// /// A string. public override string ToString() { return "(" + X + ", " + Y + ", " + Z + ")"; } /// /// An implementation of of that allows for the /// Y coordinates of two vertices to be within a specified range and still be considered equal. /// internal class RoughYEqualityComparer : IEqualityComparer { private const int HashConstX = unchecked((int)0x8da6b343); private const int HashConstZ = unchecked((int)0xcb1ab31f); private int epsilonY; /// /// Initializes a new instance of the class. /// /// The range of Y values in which two vertices are considered equal. public RoughYEqualityComparer(int epsilonY) { this.epsilonY = epsilonY; } /// /// Compares two vertices for equality. /// /// A vertex. /// Another vertex. /// A value indicating whether the two vertices are equal. public bool Equals(PolyVertex left, PolyVertex right) { return left.X == right.X && (Math.Abs(left.Y - right.Y) <= epsilonY) && left.Z == right.Z; } /// /// Gets a unique hash code for the contents of a instance. /// /// A vertex. /// A hash code. public int GetHashCode(PolyVertex obj) { return HashConstX * obj.X + HashConstZ * obj.Z; } } } }