public void HashOfHull3d_Invalid_NotEquals_Empty() { var a = Hull3d.Invalid; var b = new Hull3d(new Plane3d[0]); Assert.IsTrue(a.ComputeMd5Hash() != b.ComputeMd5Hash()); }
public void ComputeCorners_Frustum() { var hull = new Hull3d(new[] { new Plane3d(new V3d(0, 0, -1), V3d.OOO), new Plane3d(new V3d(0, 0, +1), new V3d(0, 0, 0.25)), new Plane3d(new V3d(-1, 0, +1).Normalized, V3d.OOO), new Plane3d(new V3d(+1, 0, +1).Normalized, V3d.IOO), new Plane3d(new V3d(0, -1, +1).Normalized, V3d.OOO), new Plane3d(new V3d(0, +1, +1).Normalized, V3d.OIO), }); var expectedCorners = new [] { V3d.OOO, V3d.IOO, V3d.IIO, V3d.OIO, new V3d(0.25, 0.25, 0.25), new V3d(0.75, 0.25, 0.25), new V3d(0.75, 0.75, 0.25), new V3d(0.25, 0.75, 0.25), }; var corners = hull.ComputeCorners(); Assert.IsTrue(corners.SetEquals(expectedCorners)); }
public void HashOfHull3d_FromBox_NotEquals_FromDifferentBox() { var a = Hull3d.Create(new Box3d(new V3d(1, 2, 3), new V3d(2, 3, 4.1))); var b = Hull3d.Create(new Box3d(new V3d(1, 2, 3), new V3d(2, 3, 4.2))); Assert.IsTrue(a.ComputeMd5Hash() != b.ComputeMd5Hash()); }
public void HashOfHull3d_FromBox_Equals_FromSameBox() { var a = Hull3d.Create(new Box3d(new V3d(1, 2, 3), new V3d(2, 3, 4.1))); var b = Hull3d.Create(new Box3d(new V3d(1, 2, 3), new V3d(2, 3, 4.1))); Assert.IsTrue(a.ComputeMd5Hash() == b.ComputeMd5Hash()); }
/// <summary> /// Counts points inside convex hull (approximately). /// Result is always equal or greater than exact number. /// </summary> internal static long CountPointsApproximatelyInsideConvexHull( this PointSetNode self, Hull3d query, int minCellExponent = int.MinValue ) => CountPointsApproximately(self, n => query.Contains(n.BoundingBox), n => !query.Intersects(n.BoundingBox), minCellExponent);
/// <summary> /// Counts points outside convex hull (approximately). /// Result is always equal or greater than exact number. /// </summary> internal static long CountPointsApproximatelyOutsideConvexHull( this IPointCloudNode self, Hull3d query, int minCellExponent = int.MinValue ) => CountPointsApproximately(self, n => !query.Intersects(n.BoundingBoxExactGlobal), n => query.Contains(n.BoundingBoxExactGlobal), minCellExponent);
public void HashOfHull3d_Empty_Equals_Empty() { var a = new Hull3d(new Plane3d[0]); var b = new Hull3d(new Plane3d[0]); Assert.IsTrue(a.ComputeMd5Hash() == b.ComputeMd5Hash()); }
public void HashOfHull3d_DefaultConstructorCreatesInvalidHull3d() { var a = new Hull3d(); var b = Hull3d.Invalid; Assert.IsTrue(a.ComputeMd5Hash() == b.ComputeMd5Hash()); }
/// <summary> /// Calls action for each (node, fullyInside) in this tree that is intersecting the given hull. /// </summary> public static IEnumerable <CellQueryResult> ForEachNodeIntersecting( this IPointCloudNode self, Hull3d hull, bool doNotTraverseSubnodesWhenFullyInside, int minCellExponent = int.MinValue ) { if (self == null) { yield break; } if (self.Cell.Exponent < minCellExponent) { yield break; } for (var i = 0; i < hull.PlaneCount; i++) { if (!self.IntersectsNegativeHalfSpace(hull.PlaneArray[i])) { yield break; } } bool fullyInside = true; for (var i = 0; i < hull.PlaneCount; i++) { if (!self.InsideNegativeHalfSpace(hull.PlaneArray[i])) { fullyInside = false; break; } } yield return(new CellQueryResult(self, fullyInside)); if (fullyInside && doNotTraverseSubnodesWhenFullyInside) { yield break; } if (self.Subnodes == null) { yield break; } for (var i = 0; i < 8; i++) { var n = self.Subnodes[i]; if (n == null) { continue; } var xs = ForEachNodeIntersecting(n.Value, hull, doNotTraverseSubnodesWhenFullyInside, minCellExponent); foreach (var x in xs) { yield return(x); } } }
public void ForEachNodeIntersecting_Works() { var storage = PointCloud.CreateInMemoryStore(); var pointcloud = CreateClusteredPointsInUnitCube(1000, 10); var ns = pointcloud.Root.Value.ForEachNodeIntersecting(Hull3d.Create(Box3d.Unit), true).ToArray(); Assert.IsTrue(ns.Length > 0); }
public static FilterInsideConvexHull3d Deserialize(JObject json) { var arr = (JArray)json["Array"]; var planes = arr.Map(jt => new Plane3d(V3d.Parse((string)jt["Normal"]), V3d.Parse((string)jt["Point"]))); var hull = new Hull3d(planes); return(new FilterInsideConvexHull3d(hull)); }
/// <summary> /// All points inside convex hull (including boundary). /// </summary> public static IEnumerable <Chunk> QueryPointsInsideConvexHull( this IPointCloudNode self, Hull3d query, int minCellExponent = int.MinValue ) => QueryPoints(self, n => query.Contains(n.BoundingBoxExactGlobal), n => !query.Intersects(n.BoundingBoxExactGlobal), p => query.Contains(p), minCellExponent);
public void TestConvexClipped() { var points = new[] { V3d.OOO, V3d.IOO, V3d.OIO }; var poly = new Polygon3d(points); var box = Box3d.FromMinAndSize(-V3d.OOI, new V3d(1, 0.5, 2)); var newHull = Hull3d.Create(box).Reversed(); // requires non-intuitive reversed (or using obsolte Hull3d constructor that points inside) var polyClipped = poly.ConvexClipped(newHull); // will return positive part of planes (outside of Hull3d) var clippedBox = polyClipped.BoundingBox3d; var test = new Box3d(0, 0, 0, 1, 0.5, 0); Test.IsTrue(clippedBox == test); }
/// <summary>Computes MD5 hash of given data.</summary> public static Guid ComputeMd5Hash(this Hull3d hull) { if (hull.PlaneArray == null) { return(Guid.Empty); } return(ComputeMd5Hash(bw => { foreach (var plane in hull.PlaneArray) { bw.Write(plane.Point.X); bw.Write(plane.Point.Y); bw.Write(plane.Point.Z); bw.Write(plane.Distance); } })); }
public void VisualHullTest() { var view = Trafo3d.ViewTrafoRH(V3d.III, V3d.OOI, V3d.IOO); var proj = Trafo3d.PerspectiveProjectionOpenGl(-1, 1, -1, 1, 1, 100); var vpTrafo = view * proj; var frustumCorners = new Box3d(-V3d.III, V3d.III).ComputeCorners(); //Min, 0 near left bottom //new V3d(Max.X, Min.Y, Min.Z), 1 near right bottom //new V3d(Min.X, Max.Y, Min.Z), 2 near left top //new V3d(Max.X, Max.Y, Min.Z), 3 near right top //new V3d(Min.X, Min.Y, Max.Z), 4 far left bottom //new V3d(Max.X, Min.Y, Max.Z), 5 far right bottom //new V3d(Min.X, Max.Y, Max.Z), 6 far left top //Max 7 far right top // use inverse view-projection to get vertices in world space frustumCorners.Apply(c => vpTrafo.Backward.TransformPosProj(c)); // hull planes should point outside, assume right-handed transformation to build planes var refHull = new Hull3d(new[] { new Plane3d(frustumCorners[0], frustumCorners[2], frustumCorners[4]), // left new Plane3d(frustumCorners[1], frustumCorners[5], frustumCorners[3]), // right new Plane3d(frustumCorners[0], frustumCorners[4], frustumCorners[1]), // bottom new Plane3d(frustumCorners[2], frustumCorners[3], frustumCorners[6]), // top new Plane3d(frustumCorners[0], frustumCorners[1], frustumCorners[2]), // near new Plane3d(frustumCorners[4], frustumCorners[6], frustumCorners[5]), // far }); var newHull = vpTrafo.GetVisualHull(); for (int i = 0; i < 6; i++) { Report.Line("OLD: {0} NEW: {1}", newHull.PlaneArray[i], refHull.PlaneArray[i]); } // camera position should have height 1.0 from near-plane var hRef = refHull.PlaneArray[4].Height(view.GetViewPosition()); var hNew = newHull.PlaneArray[4].Height(view.GetViewPosition()); Assert.True(hRef.ApproximateEquals(1.0, 1e-7) && hRef.ApproximateEquals(hNew, 1e-7)); for (int i = 0; i < 6; i++) { Assert.True(newHull.PlaneArray[i].Coefficients.ApproxEqual(refHull.PlaneArray[i].Coefficients, 1e-7)); } }
public static Box3d IntersectionBounds(this Hull3d hull, Box3d box) { if (box.IsInvalid) { return(box); } var bh = Hull3d.Create(box); var pp = new Plane3d[6 + hull.PlaneCount]; bh.PlaneArray.CopyTo(pp, 0); hull.PlaneArray.CopyTo(pp, 6); var h = new Hull3d(pp); return(new Box3d(h.ComputeCorners())); }
/// <summary> /// Returns points inside view frustum (defined by viewProjection and canonicalViewVolume). /// </summary> public static IEnumerable <Chunk> QueryPointsInViewFrustum( this PointSet self, M44d viewProjection, Box3d canonicalViewVolume ) { var t = viewProjection.Inverse; var cs = canonicalViewVolume.ComputeCorners().Map(t.TransformPosProj); var hull = new Hull3d(new[] { new Plane3d(cs[0], cs[2], cs[1]), // near new Plane3d(cs[5], cs[7], cs[4]), // far new Plane3d(cs[0], cs[1], cs[4]), // bottom new Plane3d(cs[1], cs[3], cs[5]), // left new Plane3d(cs[4], cs[6], cs[0]), // right new Plane3d(cs[3], cs[2], cs[7]), // top }); return(QueryPointsInsideConvexHull(self, hull)); }
public void ComputeCorners_Cube() { var hull = new Hull3d(new[] { new Plane3d(-V3d.XAxis, V3d.Zero), new Plane3d(-V3d.YAxis, V3d.Zero), new Plane3d(-V3d.ZAxis, V3d.Zero), new Plane3d(V3d.XAxis, V3d.One), new Plane3d(V3d.YAxis, V3d.One), new Plane3d(V3d.ZAxis, V3d.One) }); var corners = hull.ComputeCorners(); var expectedCorners = new [] { V3d.OOO, V3d.OOI, V3d.OIO, V3d.OII, V3d.IOO, V3d.IOI, V3d.IIO, V3d.III }; Assert.IsTrue(corners.SetEquals(expectedCorners)); }
/// <summary> /// Counts points outside convex hull. /// </summary> internal static long CountPointsOutsideConvexHull( this IPointCloudNode self, Hull3d query, int minCellExponent = int.MinValue ) => CountPointsInsideConvexHull(self, query.Reversed(), minCellExponent);
/// <summary> /// Counts points outside convex hull. /// </summary> internal static long CountPointsOutsideConvexHull( this PointSet self, Hull3d query, int minCellExponent = int.MinValue ) => CountPointsOutsideConvexHull(self.Root.Value, query, minCellExponent);
/// <summary> /// All points outside convex hull (excluding boundary). /// </summary> public static IEnumerable <Chunk> QueryPointsOutsideConvexHull( this IPointCloudNode self, Hull3d query, int minCellExponent = int.MinValue ) => QueryPointsInsideConvexHull(self, query.Reversed(), minCellExponent);
/// <summary> /// All points outside convex hull (excluding boundary). /// </summary> public static IEnumerable <Chunk> QueryPointsOutsideConvexHull( this PointSet self, Hull3d query, int minCellExponent = int.MinValue ) => QueryPointsOutsideConvexHull(self.Root.Value, query, minCellExponent);
/// <summary> /// Calls action for each (node, fullyInside) in this pointset, that is intersecting the given hull. /// </summary> public static void ForEachIntersectingNode( this IPointCloudNode self, bool outOfCore, Hull3d hull, bool doNotTraverseSubnodesWhenFullyInside, Action <IPointCloudNode, bool> action, CancellationToken ct = default ) { ct.ThrowIfCancellationRequested(); for (var i = 0; i < hull.PlaneCount; i++) { if (!self.IntersectsNegativeHalfSpace(hull.PlaneArray[i])) { return; } } bool fullyInside = true; for (var i = 0; i < hull.PlaneCount; i++) { if (!self.InsideNegativeHalfSpace(hull.PlaneArray[i])) { fullyInside = false; break; } } action(self, fullyInside); if (fullyInside && doNotTraverseSubnodesWhenFullyInside) { return; } if (self.Subnodes == null) { return; } if (outOfCore) { for (var i = 0; i < 8; i++) { var n = self.Subnodes[i]; if (n != null) { n.Value.ForEachIntersectingNode(outOfCore, hull, doNotTraverseSubnodesWhenFullyInside, action, ct); } } } else { for (var i = 0; i < 8; i++) { var n = self.Subnodes[i]; if (n != null) { if (n.TryGetValue(out IPointCloudNode node)) { node.ForEachIntersectingNode(outOfCore, hull, doNotTraverseSubnodesWhenFullyInside, action, ct); } } } } }
public static bool Contains( this Hull3d self, Box3d box) { var planes = self.PlaneArray; var imax = self.PlaneCount; for (var i = 0; i < imax; i++) { if (planes[i].Height(box.Min) > 0) { return(false); } } for (var i = 0; i < imax; i++) { if (planes[i].Height(box.Max) > 0) { return(false); } } for (var i = 0; i < imax; i++) { if (planes[i].Height(new V3d(box.Max.X, box.Min.Y, box.Min.Z)) > 0) { return(false); } } for (var i = 0; i < imax; i++) { if (planes[i].Height(new V3d(box.Max.X, box.Max.Y, box.Min.Z)) > 0) { return(false); } } for (var i = 0; i < imax; i++) { if (planes[i].Height(new V3d(box.Min.X, box.Max.Y, box.Min.Z)) > 0) { return(false); } } for (var i = 0; i < imax; i++) { if (planes[i].Height(new V3d(box.Min.X, box.Min.Y, box.Max.Z)) > 0) { return(false); } } for (var i = 0; i < imax; i++) { if (planes[i].Height(new V3d(box.Max.X, box.Min.Y, box.Max.Z)) > 0) { return(false); } } for (var i = 0; i < imax; i++) { if (planes[i].Height(new V3d(box.Min.X, box.Max.Y, box.Max.Z)) > 0) { return(false); } } return(true); }
/// <summary></summary> public FilterInsideConvexHull3d(Hull3d filter) { Hull = filter; }