// David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2020 // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // Version: 4.0.2019.08.13 #pragma once #include // Separate two point sets, if possible, by computing a line for which the // point sets lie on opposite sides. The algorithm computes the convex hull // of the point sets, then uses the method of separating axes to determine // whether the two convex polygons are disjoint. // https://www.geometrictools.com/Documentation/MethodOfSeparatingAxes.pdf // The ComputeType is for the ConvexHull2 class. namespace WwiseGTE { template class SeparatePoints2 { public: // The return value is 'true' if and only if there is a separation. // If 'true', the returned line is a separating line. The code // assumes that each point set has at least 3 noncollinear points. bool operator()(int numPoints0, Vector2 const* points0, int numPoints1, Vector2 const* points1, Line2& separatingLine) const { // Construct convex hull of point set 0. ConvexHull2 ch0; ch0(numPoints0, points0, (Real)0); if (ch0.GetDimension() != 2) { return false; } // Construct convex hull of point set 1. ConvexHull2 ch1; ch1(numPoints1, points1, (Real)0); if (ch1.GetDimension() != 2) { return false; } int numEdges0 = static_cast(ch0.GetHull().size()); int const* edges0 = &ch0.GetHull()[0]; int numEdges1 = static_cast(ch1.GetHull().size()); int const* edges1 = &ch1.GetHull()[0]; // Test edges of hull 0 for possible separation of points. int j0, j1, i0, i1, side0, side1; Vector2 lineNormal; Real lineConstant; for (j1 = 0, j0 = numEdges0 - 1; j1 < numEdges0; j0 = j1++) { // Look up edge (assert: i0 != i1 ). i0 = edges0[j0]; i1 = edges0[j1]; // Compute potential separating line // (assert: (xNor,yNor) != (0,0)). separatingLine.origin = points0[i0]; separatingLine.direction = points0[i1] - points0[i0]; Normalize(separatingLine.direction); lineNormal = Perp(separatingLine.direction); lineConstant = Dot(lineNormal, separatingLine.origin); // Determine whether hull 1 is on same side of line. side1 = OnSameSide(lineNormal, lineConstant, numEdges1, edges1, points1); if (side1) { // Determine on which side of line hull 0 lies. side0 = WhichSide(lineNormal, lineConstant, numEdges0, edges0, points0); if (side0 * side1 <= 0) // Line separates hulls. { return true; } } } // Test edges of hull 1 for possible separation of points. for (j1 = 0, j0 = numEdges1 - 1; j1 < numEdges1; j0 = j1++) { // Look up edge (assert: i0 != i1 ). i0 = edges1[j0]; i1 = edges1[j1]; // Compute perpendicular to edge // (assert: (xNor,yNor) != (0,0)). separatingLine.origin = points1[i0]; separatingLine.direction = points1[i1] - points1[i0]; Normalize(separatingLine.direction); lineNormal = Perp(separatingLine.direction); lineConstant = Dot(lineNormal, separatingLine.origin); // Determine whether hull 0 is on same side of line. side0 = OnSameSide(lineNormal, lineConstant, numEdges0, edges0, points0); if (side0) { // Determine on which side of line hull 1 lies. side1 = WhichSide(lineNormal, lineConstant, numEdges1, edges1, points1); if (side0 * side1 <= 0) // Line separates hulls. { return true; } } } return false; } private: int OnSameSide(Vector2 const& lineNormal, Real lineConstant, int numEdges, int const* edges, Vector2 const* points) const { // Test whether all points on same side of line Dot(N,X) = c. Real c0; int posSide = 0, negSide = 0; for (int i1 = 0, i0 = numEdges - 1; i1 < numEdges; i0 = i1++) { c0 = Dot(lineNormal, points[edges[i0]]); if (c0 > lineConstant) { ++posSide; } else if (c0 < lineConstant) { ++negSide; } if (posSide && negSide) { // Line splits point set. return 0; } c0 = Dot(lineNormal, points[edges[i1]]); if (c0 > lineConstant) { ++posSide; } else if (c0 < lineConstant) { ++negSide; } if (posSide && negSide) { // Line splits point set. return 0; } } return (posSide ? +1 : -1); } int WhichSide(Vector2 const& lineNormal, Real lineConstant, int numEdges, int const* edges, Vector2 const* points) const { // Establish which side of line hull is on. Real c0; for (int i1 = 0, i0 = numEdges - 1; i1 < numEdges; i0 = i1++) { c0 = Dot(lineNormal, points[edges[i0]]); if (c0 > lineConstant) { // Hull on positive side. return +1; } if (c0 < lineConstant) { // Hull on negative side. return -1; } c0 = Dot(lineNormal, points[edges[i1]]); if (c0 > lineConstant) { // Hull on positive side. return +1; } if (c0 < lineConstant) { // Hull on negative side. return -1; } } // Hull is effectively collinear. return 0; } }; }