/// <summary> /// Enumerates all vertex indexes at which /// both outgoing edges meet at an angle that /// is less than the given threshold. /// </summary> public static IEnumerable <int> GetSpikes( this Polygon3d self, double toleranceInDegrees = 0.1) { if (toleranceInDegrees < 0.0) { throw new ArgumentOutOfRangeException(); } var threshold = Conversion.RadiansFromDegrees(toleranceInDegrees).Cos(); var edges = self.GetEdgeArray(); edges.Apply(e => e.Normalized); var ec = edges.Length; if (V3d.Dot(-edges[ec - 1], edges[0]) > threshold) { yield return(0); } for (int i = 1; i < ec; i++) { if (V3d.Dot(-edges[i - 1], edges[i]) > threshold) { yield return(i); } } }
/// <summary> /// Compute the triangulation of the supplied concave polygon and /// return the resulting triangle index array. /// </summary> public static int[] ComputeTriangulationOfConcavePolygon( this Polygon3d polygon, double absoluteEpsilon) { int[] triangleIndexArray = new int[3 * (polygon.PointCount - 2)]; polygon.ComputeTriangulationOfConcavePolygon( absoluteEpsilon, 0, triangleIndexArray); return(triangleIndexArray); }
public void Polygon3dBoundingBox() { var poly = new Polygon3d(V3d.OOO, V3d.IOO, V3d.IIO); var bb = poly.BoundingBox3d(0.5); Assert.IsTrue(bb.Min == new V3d(0.0, 0.0, -0.5)); Assert.IsTrue(bb.Max == new V3d(1.0, 1.0, 0.5)); }
public static double ComputeFromPointFormFactor( this Polygon3d targetPolygon, V3d sourcePoint, V3d sourceNormal, double eps = 1e-6) { return(targetPolygon.ComputeUnscaledFormFactor( sourcePoint, sourceNormal, eps) / Constant.PiTimesTwo); }
public static double ComputeToPointFormFactor( this Polygon3d sourcePolygon, double polygonArea, V3d targetPoint, V3d targetNormal, double eps = 1e-6) { return(sourcePolygon.ComputeUnscaledFormFactor( targetPoint, targetNormal, eps) / (Constant.PiTimesTwo * polygonArea)); }
public static double ComputeUnscaledFormFactor( this Polygon3d polygon, V3d p, V3d n, double eps = 1e-6) { var vc = polygon.PointCount; V3d[] cpa = new V3d[vc + 1]; var cc = 0; var pb = polygon[0] - p; var hb = V3d.Dot(pb, n); bool hbp = hb > eps, hbn = hb < -eps; if (hb >= -eps) { cpa[cc++] = pb; } var p0 = pb; var h0 = hb; var h0p = hbp; var h0n = hbn; for (int vi = 1; vi < vc; vi++) { var p1 = polygon[vi] - p; var h1 = V3d.Dot(p1, n); bool h1p = h1 > eps, h1n = h1 < -eps; if (h0p && h1n || h0n && h1p) { cpa[cc++] = p0 + (p1 - p0) * (h0 / (h0 - h1)); } if (h1 >= -eps) { cpa[cc++] = p1; } p0 = p1; h0 = h1; h0p = h1p; h0n = h1n; } if (h0p && hbn || h0n && hbp) { cpa[cc++] = p0 + (pb - p0) * (h0 / (h0 - hb)); } var cpr = cpa.Map(cc, v => v.Length); var cv = V3d.Cross(cpa[0], cpa[cc - 1]); double ff = V3d.Dot(n, cv) * Fun.AcosC(V3d.Dot(cpa[0], cpa[cc - 1]) / (cpr[0] * cpr[cc - 1])) / cv.Length; for (int ci = 0; ci < cc - 1; ci++) { cv = V3d.Cross(cpa[ci + 1], cpa[ci]); ff += V3d.Dot(n, cv) * Fun.AcosC(V3d.Dot(cpa[ci + 1], cpa[ci]) / (cpr[ci + 1] * cpr[ci])) / cv.Length; } return(ff); }
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); }
public static Box3d BoundingBox3d( this Polygon3d self, double eps) { var bb = Box3d.Invalid; var v = self.ComputeNormal() * eps; foreach (var p in self.Points) { bb.ExtendBy(p + v); bb.ExtendBy(p - v); } return(bb); }
public void CanQueryPointsNotNearPolygon_3() { var q = new Polygon3d( new V3d(.0, .0, .3), new V3d(.25, .0, .3), new V3d(.5, .5, .3), new V3d(.0, .5, .3) ); var rs = CreateRegularPointsInUnitCube(4, 1) .QueryPointsNotNearPolygon(q, 0.2) .SelectMany(x => x.Positions) .ToArray() ; Assert.IsTrue(rs.Length == 64 - 2 * 3); }
/// <summary> /// Compute the triangulation of the supplied concave polygon and put /// the triangle vertex indices into the supplied array starting at /// the supplied triangle vertex index. /// </summary> /// <returns>the number of triangles in the triangulation</returns> private static int ComputeTriangulationOfConcavePolygon( this Polygon3d polygon, double absoluteEpsilon, int triangleVertexIndex, int[] triangleIndexArray) { var polyList = polygon.ComputeNonConcaveSubPolygons(absoluteEpsilon); foreach (var pia in polyList) { int tc = polygon.GetPointArray().ComputeTriangulationOfNonConcavePolygon( pia, absoluteEpsilon, triangleVertexIndex, triangleIndexArray); triangleVertexIndex += 3 * tc; } return(polygon.PointCount - 2); }
/// <summary> /// Count points approximately NOT within maxDistance of given polygon. /// Result is always equal or greater than exact number. /// Faster than CountPointsNotNearPolygon. /// </summary> public static long CountPointsApproximatelyNotNearPolygon( this IPointCloudNode node, Polygon3d polygon, double maxDistance, int minCellExponent = int.MinValue ) { var bounds = polygon.BoundingBox3d(maxDistance); var plane = polygon.GetPlane3d(); var w2p = plane.GetWorldToPlane(); var poly2d = new Polygon2d(polygon.GetPointArray().Map(p => w2p.TransformPos(p).XY)); return(CountPointsApproximately(node, n => !n.BoundingBoxExactGlobal.Intersects(bounds), n => false, minCellExponent )); }
public static List <int[]> ComputeNonConcaveSubPolygons( this Polygon3d polygon, double absoluteEpsilon) { V3d normal = polygon.ComputeDoubleAreaNormal(); double len2 = normal.LengthSquared; if (len2 < absoluteEpsilon * absoluteEpsilon) { return(new int[polygon.PointCount].SetByIndex(i => i).IntoList()); } M44d.NormalFrame(V3d.Zero, normal * (1.0 / Fun.Sqrt(len2)), out M44d local2global, out M44d global2local); var polygon2d = polygon.ToPolygon2d(p => global2local.TransformPos(p).XY); return(polygon2d.ComputeNonConcaveSubPolygons(absoluteEpsilon)); }
/// <summary> /// All points NOT within maxDistance of given polygon. /// </summary> public static IEnumerable <Chunk> QueryPointsNotNearPolygon( this IPointCloudNode node, Polygon3d polygon, double maxDistance, int minCellExponent = int.MinValue ) { var bounds = polygon.BoundingBox3d(maxDistance); var plane = polygon.GetPlane3d(); var w2p = plane.GetWorldToPlane(); var poly2d = new Polygon2d(polygon.GetPointArray().Map(p => w2p.TransformPos(p).XY)); return(QueryPoints(node, n => !n.BoundingBoxExactGlobal.Intersects(bounds), n => false, p => !polygon.Contains(plane, w2p, poly2d, maxDistance, p, out double d), minCellExponent )); }
/// <summary> /// Count points within maxDistance of given polygon. /// </summary> public static long CountPointsNearPolygon( this PointSetNode node, Polygon3d polygon, double maxDistance, int minCellExponent = int.MinValue ) { var bounds = polygon.BoundingBox3d(maxDistance); var plane = polygon.GetPlane3d(); var w2p = plane.GetWorldToPlane(); var poly2d = new Polygon2d(polygon.GetPointArray().Map(p => w2p.TransformPos(p).XY)); return(CountPoints(node, n => false, n => !n.BoundingBox.Intersects(bounds), p => polygon.Contains(plane, w2p, poly2d, maxDistance, p, out double d), minCellExponent )); }
public void CanQueryPointsNotNearPolygon_1() { var pointset = CreateRandomPointsInUnitCube(1024, 64); var q = new Polygon3d(new V3d(0.4, 0.4, 0.5), new V3d(0.6, 0.4, 0.5), new V3d(0.6, 0.6, 0.5), new V3d(0.4, 0.6, 0.5)); var ps = pointset.QueryPointsNotNearPolygon(q, 0.1).SelectMany(x => x.Positions).ToList(); Assert.IsTrue(pointset.PointCount > ps.Count); var bb = new Box3d(new V3d(0.4, 0.4, 0.4), new V3d(0.6, 0.6, 0.6)); foreach (var p in ps) { Assert.IsTrue(!bb.Contains(p)); } }
public void CanQueryPointsNearPolygon_Performance() { var sw = new Stopwatch(); var pointset = CreateRandomPointsInUnitCube(1024 * 1024, 32); var q = new Polygon3d(new V3d(0.4, 0.4, 0.5), new V3d(0.41, 0.4, 0.5), new V3d(0.41, 0.41, 0.5), new V3d(0.4, 0.41, 0.5)); var plane = new Plane3d(new V3d(0.4, 0.4, 0.5), new V3d(0.41, 0.4, 0.5), new V3d(0.41, 0.41, 0.5)); sw.Restart(); var ps0 = pointset.QueryPointsNearPolygon(q, 0.01).SelectMany(x => x.Positions).ToList(); var t0 = sw.Elapsed.TotalSeconds; sw.Restart(); var ps1 = pointset.QueryPointsNearPlane(plane, 0.01).ToList(); var t1 = sw.Elapsed.TotalSeconds; Assert.IsTrue(t0 * 10 < t1); }
public static Vector3d Support(Polygon3d hull, Vector3d direction) { Vector3d result = Vector3d.Zero; double maxdot = double.NegativeInfinity; for (int i = 0; i < hull.Count; ++i) { var point = (Vector3d)hull[i]; double dot = Vector.Dot(point, direction); if (dot > maxdot) { maxdot = dot; result = point; } } return(result); }
/// <summary> /// Enumerates all pairs of coincident vertices (as pairs of vertex indices). /// </summary> public static IEnumerable <Pair <int> > GetCoincidentPoints( this Polygon3d polygon, double toleranceAbsolute) { if (toleranceAbsolute < 0.0) { throw new ArgumentOutOfRangeException(); } var pc = polygon.PointCount; for (int pi = 0; pi < pc; pi++) { for (int pj = pi + 1; pj < pc; pj++) { if (polygon[pi].ApproxEqual(polygon[pj], toleranceAbsolute)) { yield return(Pair.Create(pi, pj)); } } } }
/// <summary> /// Computes the normal with the length of twice the polygon area as /// the sum of the simple triangulation cross-product normals. /// NOTE: This has been tested to be slightly faster and slightly more /// accurate than the computation via the 3d Newell normal /// (see Math.Tests/GeometryTests, rft 2013-05-04). /// </summary> public static V3d ComputeDoubleAreaNormal(this Polygon3d polygon) { var pc = polygon.PointCount; if (pc < 3) { return(V3d.Zero); } V3d p0 = polygon[0]; V3d e0 = polygon[1] - p0; V3d normal = V3d.Zero; for (int pi = 2; pi < pc; pi++) { var e1 = polygon[pi] - p0; normal += e0.Cross(e1); e0 = e1; } return(normal); }
/// <summary> /// The geometric center of a 3-dimensional, flat polygon. /// WARNING: UNTESTED! /// </summary> public static V3d ComputeCentroid(this Polygon3d polygon) { var pc = polygon.PointCount; if (pc < 3) { return(V3d.Zero); } V3d p0 = polygon[0], p1 = polygon[1]; V3d e0 = p1 - p0; var p2 = polygon[2]; var e1 = p2 - p0; var normal = e0.Cross(e1); var area2 = normal.Length; var centroid = area2 * (p0 + p1 + p2); p1 = p2; e0 = e1; for (int pi = 3; pi < pc; pi++) { p2 = polygon[pi]; e1 = p2 - p0; var n = e0.Cross(e1); var a2 = Fun.Sign(normal.Dot(n)) * n.Length; area2 += a2; centroid += a2 * (p0 + p1 + p2); p1 = p2; e0 = e1; } if (area2 > Constant <double> .PositiveTinyValue) { return(centroid * (Constant.OneThird / area2)); } else if (area2 < Constant <double> .NegativeTinyValue) { return(centroid * (-Constant.OneThird / area2)); } else { return(V3d.Zero); } }
/// <summary> /// Enumerates all pairs of edges that intersect (as pairs of edge indices). /// </summary> public static IEnumerable <Pair <int> > GetSelfIntersections( this Polygon3d polygon, double toleranceAbsolute) { if (toleranceAbsolute < 0.0) { throw new ArgumentOutOfRangeException(); } var pc = polygon.PointCount; var la = polygon.GetEdgeLineArray(); for (int i = 0; i < pc; i++) { int jmax = (i > 0) ? pc : pc - 1; for (int j = i + 2; j < jmax; j++) { if (la[i].Intersects(la[j], toleranceAbsolute)) { yield return(Pair.Create(i, j)); } } } }
/// <summary> /// Computes the supporting plane of the polygon via the vertex centroid /// and the sum of the simple triangulation cross-product normals. /// NOTE: This has been tested to be slightly faster and slightly more /// accurate than the computation via the 3d Newell normal /// (see Math.Tests/GeometryTests, rft 2013-05-04). /// </summary> public static Plane3d ComputePlane3d(this Polygon3d polygon) { return(new Plane3d(polygon.ComputeNormal(), polygon.ComputeVertexCentroid())); }
/// <summary> /// Computes the normalized normal as the sum of the simple /// triangulation cross-product normals. /// NOTE: This has been tested to be slightly faster and slightly more /// accurate than the computation via the 3d Newell normal /// (see Math.Tests/GeometryTests, rft 2013-05-04). /// </summary> public static V3d ComputeNormal(this Polygon3d polygon) { return(polygon.ComputeDoubleAreaNormal().Normalized); }
/// <summary> /// Returns true if at least one vertex /// both outgoing edges meet at an angle that /// is less than the given threshold. /// </summary> public static bool HasSpikes( this Polygon3d self, double toleranceInDegrees = 0.1) { return(GetSpikes(self, toleranceInDegrees).Any()); }
/// <summary> /// Computes the area as the length of the sum of the simple /// triangulation cross-product normals. /// NOTE: This has been tested to be slightly faster and slightly more /// accurate than the computation via the 3d Newell normal /// (see Math.Tests/GeometryTests, rft 2013-05-04). /// </summary> public static double ComputeArea(this Polygon3d polygon) { return(0.5 * polygon.ComputeDoubleAreaNormal().Length); }
public static V3d ComputeNormal(this Polygon3d polygon) => polygon.ComputeDoubleAreaNormal().Normalized;
/// <summary> /// Returns true if at least two edges intersect. /// </summary> public static bool IsSelfIntersecting( this Polygon3d self, double toleranceAbsolute = 1e-10) { return(GetSelfIntersections(self, toleranceAbsolute).Any()); }
/// <summary> /// Returns true if at least two vertices are coincident. /// </summary> public static bool HasCoincidentPoints( this Polygon3d polygon, double toleranceAbsolute) { return(GetCoincidentPoints(polygon, toleranceAbsolute).Any()); }
/// <summary> /// Returns the plane through the first 3 points of the polygon. /// </summary> public static Plane3d GetPlane3d(this Polygon3d polygon) { return(new Plane3d(polygon[0], polygon[1], polygon[2])); }
public static Polygon2d ToPolygon2d( this Polygon3d polygon, Func <V3d, int, V2d> point_index_copyFun) { return(new Polygon2d(polygon.GetPointArray(point_index_copyFun))); }