/// <summary> /// /// </summary> /// <typeparam name="T"></typeparam> /// <param name="items"></param> /// <param name="getPosition"></param> /// <param name="grid"></param> /// <param name="indexMap"></param> /// <param name="tolerance"></param> /// <returns></returns> public static List <T> RemoveCoincident <T>(IReadOnlyList <T> items, Func <T, Vec3d> getPosition, HashGrid3d <int> grid, out int[] indexMap, double tolerance = SlurMath.ZeroTolerance) { var result = new List <T>(); var map = new int[items.Count]; grid.Scale = tolerance * RadiusToHashScale; // add points to result if no duplicates are found for (int i = 0; i < items.Count; i++) { var p0 = getPosition(items[i]); // search for concident in result foreach (int j in grid.Search(new Interval3d(p0, tolerance))) { if (p0.ApproxEquals(getPosition(result[j]), tolerance)) { map[i] = j; goto EndFor; } } // no coincident item found, add i to result grid.Insert(p0, result.Count); map[i] = result.Count; result.Add(items[i]); EndFor :; } indexMap = map; return(result); }
/// <summary> /// For each item in A, returns the index of the first coincident item in B. If no coincident point is found, -1 is returned. /// </summary> /// <typeparam name="T"></typeparam> /// <typeparam name="U"></typeparam> /// <param name="itemsA"></param> /// <param name="getPositionA"></param> /// <param name="itemsB"></param> /// <param name="getPositionB"></param> /// <param name="grid"></param> /// <param name="tolerance"></param> /// <returns></returns> public static IEnumerable <int> GetFirstCoincident <T, U>(IReadOnlyList <T> itemsA, Func <T, Vec3d> getPositionA, IReadOnlyList <U> itemsB, Func <U, Vec3d> getPositionB, HashGrid3d <int> grid, double tolerance = SlurMath.ZeroTolerance) { grid.Scale = tolerance * RadiusToHashScale; // insert B for (int i = 0; i < itemsB.Count; i++) { grid.Insert(getPositionB(itemsB[i]), i); } // search from A for (int i = 0; i < itemsA.Count; i++) { var p0 = getPositionA(itemsA[i]); foreach (var j in grid.Search(new Interval3d(p0, tolerance))) { if (p0.ApproxEquals(getPositionB(itemsB[j]), tolerance)) { yield return(j); goto EndFor; } } yield return(-1); EndFor :; } }
/// <summary> /// For each item, returns the index of the first coincident item within the same list. If no coincident point is found, -1 is returned. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="items"></param> /// <param name="grid"></param> /// <param name="getPosition"></param> /// <param name="tolerance"></param> /// <returns></returns> public static IEnumerable <int> GetFirstCoincident <T>(IReadOnlyList <T> items, Func <T, Vec3d> getPosition, HashGrid3d <int> grid, double tolerance = 1.0e-8) { grid.BinScale = tolerance * RadiusToBinScale; // insert for (int i = 0; i < items.Count; i++) { grid.Insert(getPosition(items[i]), i); } // search for (int i = 0; i < items.Count; i++) { var p0 = getPosition(items[i]); foreach (var j in grid.Search(new Interval3d(p0, tolerance))) { if (i != j && p0.ApproxEquals(getPosition(items[j]), tolerance)) { yield return(j); goto EndFor; } } yield return(-1); EndFor :; } }
/// <summary> /// For each item in A, returns the index of each coincident items in B. /// </summary> /// <typeparam name="T"></typeparam> /// <typeparam name="U"></typeparam> /// <param name="itemsA"></param> /// <param name="getPositionA"></param> /// <param name="itemsB"></param> /// <param name="getPositionB"></param> /// <param name="grid"></param> /// <param name="tolerance"></param> /// <returns></returns> public static IEnumerable <IEnumerable <int> > GetAllCoincident <T, U>(IReadOnlyList <T> itemsA, Func <T, Vec3d> getPositionA, IReadOnlyList <U> itemsB, Func <U, Vec3d> getPositionB, HashGrid3d <int> grid, double tolerance = SlurMath.ZeroTolerance) { grid.Scale = tolerance * RadiusToHashScale; // insert B for (int i = 0; i < itemsB.Count; i++) { grid.Insert(getPositionB(itemsB[i]), i); } // search from A for (int i = 0; i < itemsA.Count; i++) { var p0 = getPositionA(itemsA[i]); yield return(grid.Search(new Interval3d(p0, tolerance)) .Where(j => p0.ApproxEquals(getPositionB(itemsB[j]), tolerance))); } }
/// <summary> /// Consolidates points within a given search radius. /// Returns true if the solution converged within the given maximum number of steps. /// </summary> /// <param name="points"></param> /// <param name="radius"></param> /// <param name="tolerance"></param> /// <param name="maxSteps"></param> /// <returns></returns> public static bool ConsolidatePoints(IList <Vec3d> points, double radius, double tolerance = SlurMath.ZeroTolerance, int maxSteps = 4) { var grid = new HashGrid3d <Vec3d>(radius * RadiusToHashScale, points.Count); var radSqr = radius * radius; var tolSqr = tolerance * tolerance; var converged = false; while (!converged && maxSteps-- > 0) { converged = true; // insert into grid for (int i = 0; i < points.Count; i++) { var p = points[i]; grid.Insert(p, p); } // search grid for (int i = 0; i < points.Count; i++) { var p0 = points[i]; var p1 = grid.Search(new Interval3d(p0, radius)) .Where(p => p0.SquareDistanceTo(p) < radSqr) .Mean(); points[i] = p1; if (p0.SquareDistanceTo(p1) > tolSqr) { converged = false; } } grid.Clear(); } return(maxSteps > 0); }