public void CopyConstructor() { // Try copying empty mesh. var a = new DcelMesh(null); var b = new DcelMesh(new DcelMesh()); // Create a mesh. var mesh = DcelMesh.CreateCube(); mesh.CutConvex(new Plane(new Vector3F(1, 2, 3).Normalized, 0.6f)); mesh.CutConvex(new Plane(new Vector3F(-2, -3, 1).Normalized, 0.8f)); // Create clone with copy constructor and compare. var clone = new DcelMesh(mesh); Assert.AreEqual(mesh.Vertices.Count, clone.Vertices.Count); Assert.AreEqual(mesh.Edges.Count, clone.Edges.Count); Assert.AreEqual(mesh.Faces.Count, clone.Faces.Count); var tm = mesh.ToTriangleMesh(); var tmc = clone.ToTriangleMesh(); for (int i = 0; i < tm.Vertices.Count; i++) { Assert.AreEqual(tm.Vertices[i], tmc.Vertices[i]); } for (int i = 0; i < tm.Indices.Count; i++) { Assert.AreEqual(tm.Indices[i], tmc.Indices[i]); } }
public void IsConvex() { DcelMesh mesh = new DcelMesh(); Assert.IsFalse(mesh.IsConvex()); mesh.Vertex = new DcelVertex(new Vector3F(1, 2, 3), null); Assert.IsTrue(mesh.IsConvex()); mesh = DcelMesh.FromTriangleMesh(new BoxShape(1, 2, 3).GetMesh(0.01f, 1)); Assert.IsTrue(mesh.IsConvex()); // Remove fifth triangle. var triangleMesh = new BoxShape(1, 2, 3).GetMesh(0.01f, 1); triangleMesh.Indices.RemoveRange(4 * 3, 3); mesh = DcelMesh.FromTriangleMesh(triangleMesh); Assert.IsFalse(mesh.IsConvex()); mesh = DcelMesh.FromTriangleMesh(new SphereShape(10).GetMesh(0.01f, 3)); Assert.IsTrue(mesh.IsConvex()); // Move any sphere vertex more inside. var v = mesh.Vertices[10]; v.Position = v.Position * 0.9f; Assert.IsFalse(mesh.IsConvex()); }
public void CutConvex() { var mesh = new BoxShape(1, 2, 3).GetMesh(0.01f, 1); var dcel = DcelMesh.FromTriangleMesh((ITriangleMesh)mesh); Assert.IsTrue(dcel.IsValid()); Assert.IsTrue(dcel.IsConvex()); Assert.IsTrue(dcel.IsClosed()); bool result = dcel.CutConvex(new Plane(new Vector3F(0, 0, 1), 1.5f)); Assert.IsFalse(result); result = dcel.CutConvex(new Plane(new Vector3F(0, 0, 1), -1.5f)); Assert.IsTrue(result); Assert.IsNull(dcel.Vertex); Assert.AreEqual(0, dcel.Vertices.Count); dcel = DcelMesh.FromTriangleMesh((ITriangleMesh)mesh); result = dcel.CutConvex(new Plane(new Vector3F(0, 0, 1), 1f)); Assert.IsTrue(result); Assert.IsTrue(dcel.IsValid()); Assert.IsTrue(dcel.IsConvex()); Assert.IsTrue(dcel.IsClosed()); Assert.AreEqual(12, dcel.Vertices.Count); Assert.AreEqual(6 + 8 + 7 * 4, dcel.Edges.Count); // Bottom = 6 edges, new cap = 8 edges, cut sides = each 7 edges. Assert.AreEqual(12 - 1, dcel.Faces.Count); }
public void FromTriangleMesh2() { var mesh = new TriangleMesh(); mesh.Vertices.Add(new Vector3F(0, 0, 0)); mesh.Vertices.Add(new Vector3F(1, 1, 1)); mesh.Vertices.Add(new Vector3F(2, 2, 2)); mesh.Vertices.Add(new Vector3F(3, 3, 3)); //mesh.Vertices.Add(new Vector3F(405, 322, 0)); mesh.Indices.Add(0); mesh.Indices.Add(1); mesh.Indices.Add(2); mesh.Indices.Add(1); mesh.Indices.Add(3); mesh.Indices.Add(2); var dcel = DcelMesh.FromTriangleMesh((ITriangleMesh)mesh); Assert.AreEqual(4, dcel.Vertices.Count); Assert.AreEqual(2, dcel.Faces.Count); Assert.AreEqual(10, dcel.Edges.Count); Assert.IsTrue(dcel.IsValid()); Assert.IsFalse(dcel.IsClosed()); Assert.IsTrue(dcel.IsTriangleMesh()); }
/// <summary> /// Called when a mesh should be generated for the shape. /// </summary> /// <param name="absoluteDistanceThreshold">The absolute distance threshold.</param> /// <param name="iterationLimit">The iteration limit.</param> /// <returns>The triangle mesh for this shape.</returns> /// <remarks> /// <para> /// <see cref="ConvexShape"/> provides a base implementation for <see cref="OnGetMesh"/> that /// samples the support mapping and automatically generates a mesh. But derived classes should /// override <see cref="OnGetMesh"/> if they can provide a more efficient implementation. /// </para> /// </remarks> protected override TriangleMesh OnGetMesh(float absoluteDistanceThreshold, int iterationLimit) { // Use SupportMappingSampler to get surface points. IList <Vector3F> points = GeometryHelper.SampleConvexShape(this, absoluteDistanceThreshold, iterationLimit); DcelMesh mesh = GeometryHelper.CreateConvexHull(points); return(mesh.ToTriangleMesh()); }
public void FromTriangleMeshNotSupported() { var mesh = new BoxShape(1, 2, 3).GetMesh(0.01f, 1); // Add a stray triangle. - Only fully connected meshes are supported! mesh.Add(new Triangle(new Vector3F(10, 10, 10), new Vector3F(20, 20, 20), new Vector3F(30, 30, 30)), false); var dcel = DcelMesh.FromTriangleMesh((ITriangleMesh)mesh); }
public void CreateCube() { var dcel = DcelMesh.CreateCube(); Assert.IsTrue(dcel.IsValid()); Assert.IsTrue(dcel.IsConvex()); Assert.IsTrue(dcel.IsClosed()); Assert.AreEqual(8, dcel.Vertices.Count); Assert.AreEqual(4 * 6, dcel.Edges.Count); // Bottom = 6 edges, new cap = 8 edges, cut sides = each 7 edges. Assert.AreEqual(6, dcel.Faces.Count); }
public void FromTriangleMesh() { var mesh = new BoxShape(1, 2, 3).GetMesh(0.01f, 1); var dcel = DcelMesh.FromTriangleMesh((ITriangleMesh)mesh); Assert.IsTrue(dcel.IsValid()); Assert.IsTrue(dcel.IsClosed()); Assert.IsTrue(dcel.IsTriangleMesh()); Assert.AreEqual(8, dcel.Vertices.Count); Assert.AreEqual(12, dcel.Faces.Count); Assert.AreEqual(36, dcel.Edges.Count); }
public ConvexHullSample(Microsoft.Xna.Framework.Game game) : base(game) { SampleFramework.IsMouseVisible = false; GraphicsScreen.ClearBackground = true; GraphicsScreen.BackgroundColor = Color.CornflowerBlue; SetCamera(new Vector3F(0, 1, 10), 0, 0); // Generate random points. var points = new List <Vector3F>(); for (int i = 0; i < 100; i++) { points.Add(RandomHelper.Random.NextVector3F(-1, 1)); } // Apply random transformation to points to make this sample more interesting. Matrix44F transform = new Matrix44F( Matrix33F.CreateRotation(RandomHelper.Random.NextQuaternionF()) * Matrix33F.CreateScale(RandomHelper.Random.NextVector3F(0.1f, 2f)), RandomHelper.Random.NextVector3F(-1, 1)); for (int i = 0; i < points.Count; i++) { points[i] = transform.TransformPosition(points[i]); } // Compute convex hull. The result is the mesh of the hull represented as a // Doubly-Connected Edge List (DCEL). DcelMesh convexHull = GeometryHelper.CreateConvexHull(points); // We don't need the DCEL representation. Let's store the hull as a simpler triangle mesh. TriangleMesh convexHullMesh = convexHull.ToTriangleMesh(); // Compute a tight-fitting oriented bounding box. Vector3F boundingBoxExtent; // The bounding box dimensions (widths in X, Y and Z). Pose boundingBoxPose; // The pose (world space position and orientation) of the bounding box. GeometryHelper.ComputeBoundingBox(points, out boundingBoxExtent, out boundingBoxPose); // (Note: The GeometryHelper also contains methods to compute a bounding sphere.) var debugRenderer = GraphicsScreen.DebugRenderer; foreach (var point in points) { debugRenderer.DrawPoint(point, Color.White, true); } debugRenderer.DrawShape(new TriangleMeshShape(convexHullMesh), Pose.Identity, Vector3F.One, Color.Violet, false, false); debugRenderer.DrawBox(boundingBoxExtent.X, boundingBoxExtent.Y, boundingBoxExtent.Z, boundingBoxPose, Color.Red, true, false); }
public void ModifyConvexVertexLimit() { var mesh = DcelMesh.CreateCube(); mesh.CutConvex(new Plane(new Vector3F(1, 2, 3).Normalized, 0.6f)); mesh.CutConvex(new Plane(new Vector3F(-2, -3, 1).Normalized, 0.8f)); Assert.IsTrue(mesh.Vertices.Count > 10); mesh.ModifyConvex(10, 0.3f); Assert.IsTrue(mesh.IsConvex()); Assert.IsTrue(mesh.Vertices.Count <= 10); }
public void FromTriangleMeshWithOpenMesh() { var mesh = new BoxShape(1, 2, 3).GetMesh(0.01f, 1); // Remove fifth triangle. mesh.Indices.RemoveRange(4 * 3, 3); var dcel = DcelMesh.FromTriangleMesh((ITriangleMesh)mesh); Assert.IsTrue(dcel.IsValid()); Assert.IsFalse(dcel.IsClosed()); Assert.IsTrue(dcel.IsTriangleMesh()); Assert.AreEqual(8, dcel.Vertices.Count); Assert.AreEqual(11, dcel.Faces.Count); Assert.AreEqual(36, dcel.Edges.Count); }
public void CreateConvexHull1() { DcelMesh mesh = new DcelMesh(); mesh = GeometryHelper.CreateConvexHull(new[] { new Vector3F(0, 0, 0), new Vector3F(0, 0, 0), new Vector3F(1, 0, 0), new Vector3F(1, 1, 0), new Vector3F(0, 0, 1), new Vector3F(0, 0, -1), new Vector3F(-1, 1, 0), new Vector3F(-1, -1, 0) }); Assert.AreEqual(6, mesh.Vertices.Count); Assert.AreEqual(24, mesh.Edges.Count); Assert.AreEqual(8, mesh.Faces.Count); }
public void ToTriangleMesh() { // Build DCEL mesh for tetrahedron. var vertices = new[] { new Vector3F(0, 0, 0), new Vector3F(1, 0, 0), new Vector3F(0, 1, 0), new Vector3F(0, 0, 1) }; DcelMesh dcelMesh = GeometryHelper.CreateConvexHull(vertices); TriangleMesh triangleMesh = dcelMesh.ToTriangleMesh(); Assert.AreEqual(4, triangleMesh.NumberOfTriangles); Assert.IsTrue(new List <Vector3F>(triangleMesh.Vertices).Contains(new Vector3F(0, 0, 0))); Assert.IsTrue(new List <Vector3F>(triangleMesh.Vertices).Contains(new Vector3F(1, 0, 0))); Assert.IsTrue(new List <Vector3F>(triangleMesh.Vertices).Contains(new Vector3F(0, 1, 0))); Assert.IsTrue(new List <Vector3F>(triangleMesh.Vertices).Contains(new Vector3F(0, 0, 1))); }
public void TestDcelMeshDirty() { DcelMesh mesh = new DcelMesh(); DcelVertex v0 = new DcelVertex(); DcelVertex v1 = new DcelVertex(); DcelVertex v2 = new DcelVertex(); DcelEdge e0 = new DcelEdge(); DcelEdge e1 = new DcelEdge(); DcelEdge e2 = new DcelEdge(); DcelFace f = new DcelFace(); // Link features v0.Edge = e0; e0.Origin = v0; mesh.Vertex = v0; Assert.AreEqual(1, mesh.Vertices.Count); Assert.AreEqual(1, mesh.Edges.Count); Assert.AreEqual(0, mesh.Faces.Count); // Link more e0.Next = e1; e1.Next = e2; mesh.Dirty = true; Assert.AreEqual(3, mesh.Edges.Count); e1.Origin = v1; e2.Origin = v2; mesh.Dirty = true; Assert.AreEqual(3, mesh.Vertices.Count); e0.Face = f; f.Boundary = e1; mesh.Dirty = true; Assert.AreEqual(1, mesh.Faces.Count); }
public void CopyConstructor() { // Try copying empty mesh. var a = new DcelMesh(null); var b = new DcelMesh(new DcelMesh()); // Create a mesh. var mesh = DcelMesh.CreateCube(); mesh.CutConvex(new Plane(new Vector3F(1, 2, 3).Normalized, 0.6f)); mesh.CutConvex(new Plane(new Vector3F(-2, -3, 1).Normalized, 0.8f)); // Create clone with copy constructor and compare. var clone = new DcelMesh(mesh); Assert.AreEqual(mesh.Vertices.Count, clone.Vertices.Count); Assert.AreEqual(mesh.Edges.Count, clone.Edges.Count); Assert.AreEqual(mesh.Faces.Count, clone.Faces.Count); var tm = mesh.ToTriangleMesh(); var tmc = clone.ToTriangleMesh(); for (int i = 0; i < tm.Vertices.Count; i++) Assert.AreEqual(tm.Vertices[i], tmc.Vertices[i]); for (int i = 0; i < tm.Indices.Count; i++) Assert.AreEqual(tm.Indices[i], tmc.Indices[i]); }
/// <summary> /// Initializes the convex polyhedron. /// </summary> /// <param name="points">The points.</param> /// <exception cref="NotSupportedException"> /// Too many vertices in convex hull. Max. 65534 vertices in a convex polyhedron are supported. /// </exception> private void BuildConvexPolyhedron(IEnumerable <Vector3F> points) { // Build convex hull and throw away inner points. DcelMesh convexHull = GeometryHelper.CreateConvexHull(points); int numberOfVertices = (convexHull != null) ? convexHull.Vertices.Count : 0; if (numberOfVertices >= ushort.MaxValue) { throw new NotSupportedException("Too many vertices in convex hull. Max. 65534 vertices in convex hull are supported."); } _vertices = (convexHull != null) ? convexHull.Vertices.Select(v => v.Position).ToArray() : new Vector3F[0]; CacheAabb(); CacheInnerPoint(); Debug.Assert(VertexThreshold > 4, "Vertex threshold should be greater than 4."); if (numberOfVertices > VertexThreshold) { // Use an internal lookup table and vertex adjacency lists for speed up. BuildLookupTable(); BuildVertexAdjacencyLists(convexHull); } }
public void ModifyConvexSkinWidth() { // Create a mesh. var mesh = DcelMesh.CreateCube(); mesh.CutConvex(new Plane(new Vector3F(1, 2, 3).Normalized, 0.6f)); mesh.CutConvex(new Plane(new Vector3F(-2, -3, 1).Normalized, 0.8f)); var aabb = mesh.GetAabb(); var skinWidth = 0.3f; mesh.ModifyConvex(100, skinWidth); var aabb2 = mesh.GetAabb(); Assert.IsTrue(mesh.IsConvex()); Assert.IsTrue(Numeric.AreEqual(aabb.Minimum.X - skinWidth, aabb2.Minimum.X)); Assert.IsTrue(Numeric.AreEqual(aabb.Minimum.Y - skinWidth, aabb2.Minimum.Y)); Assert.IsTrue(Numeric.AreEqual(aabb.Minimum.Z - skinWidth, aabb2.Minimum.Z)); Assert.IsTrue(Numeric.AreEqual(aabb.Maximum.X + skinWidth, aabb2.Maximum.X)); Assert.IsTrue(Numeric.AreEqual(aabb.Maximum.Y + skinWidth, aabb2.Maximum.Y)); Assert.IsTrue(Numeric.AreEqual(aabb.Maximum.Z + skinWidth, aabb2.Maximum.Z)); }
public override void Update(GameTime gameTime) { var lastFileIndex = _fileIndex; if (InputService.IsPressed(Keys.NumPad7, true)) { _fileIndex++; } if (InputService.IsPressed(Keys.NumPad4, true)) { _fileIndex--; } if (_fileIndex < 0) { _fileIndex = MaxFileIndex; } if (_fileIndex > MaxFileIndex) { _fileIndex = 0; } if (lastFileIndex != _fileIndex) { LoadFile(); _pointIndex = -1; } var lastPointIndex = _pointIndex; if (InputService.IsPressed(Keys.Right, true)) { _pointIndex++; } if (InputService.IsPressed(Keys.Left, true)) { _pointIndex--; } if (_pointIndex <= 0) { _pointIndex = _points.Count; } if (_pointIndex > _points.Count) { _pointIndex = 1; } var lastSkinWidth = _skinWidth; if (InputService.IsDown(Keys.Up)) { _skinWidth *= 1.1f; } if (InputService.IsDown(Keys.Down)) { _skinWidth /= 1.1f; } if (lastPointIndex != _pointIndex || _skinWidth != lastSkinWidth) { var watch = Stopwatch.StartNew(); DcelMesh hull = GeometryHelper.CreateConvexHull(_points.Take(_pointIndex), int.MaxValue, _skinWidth); watch.Stop(); _loadTime = (float)watch.Elapsed.TotalMilliseconds; _mesh = hull.ToTriangleMesh(); _geometricObject = new GeometricObject(new TriangleMeshShape(_mesh)); } var debugRenderer = GraphicsScreen.DebugRenderer; debugRenderer.Clear(); debugRenderer.DrawAxes(Pose.Identity, 1, false); debugRenderer.DrawObject(_geometricObject, new Color(0.5f, 0.5f, 0.5f, 0.8f), false, false); debugRenderer.DrawObject(_geometricObject, Color.Black, true, true); foreach (var point in _points) { debugRenderer.DrawPoint(point, Color.White, false); } debugRenderer.DrawText( "\n\nFile: " + _fileIndex + "\n" + "Point Index: " + _pointIndex + "\n" + "Time: " + _loadTime + "\n"); }
public static void ComputeBoundingCapsule(IList <Vector3> points, out float radius, out float height, out Pose pose) { if (points == null) { throw new ArgumentNullException("points"); } // Covariance matrix. MatrixF cov = null; // ReSharper disable EmptyGeneralCatchClause try { if (points.Count > 4) { // Reduce point list to convex hull. DcelMesh dcelMesh = CreateConvexHull(points); TriangleMesh mesh = dcelMesh.ToTriangleMesh(); // Use reduced point list - if we have found a useful one. (Line objects // have not useful triangle mesh.) if (mesh.Vertices.Count > 0) { points = mesh.Vertices; } cov = ComputeCovarianceMatrixFromSurface(mesh); } } catch { } // ReSharper restore EmptyGeneralCatchClause // If anything happens in the convex hull creation, we can still go on including the // interior points and compute the covariance matrix for the points instead of the // surface. if (cov == null || Numeric.IsNaN(cov.Determinant())) { cov = ComputeCovarianceMatrixFromPoints(points); } // Perform Eigenvalue decomposition. EigenvalueDecompositionF evd = new EigenvalueDecompositionF(cov); // v transforms from local coordinate space of the capsule into world space. var v = evd.V.ToMatrix(); Debug.Assert(v.GetColumn(0).IsNumericallyNormalized); Debug.Assert(v.GetColumn(1).IsNumericallyNormalized); Debug.Assert(v.GetColumn(2).IsNumericallyNormalized); // v is like a rotation matrix, but the coordinate system is not necessarily right handed. // --> Make sure it is right-handed. v.SetColumn(2, Vector3.Cross(v.GetColumn(0), v.GetColumn(1))); // Make local Y the largest axis. (Y is the long capsule axis.) Vector3 eigenValues = evd.RealEigenvalues.ToVector3(); int largestComponentIndex = eigenValues.IndexOfLargestComponent; if (largestComponentIndex != 1) { // Swap two columns to create a right handed rotation matrix. Vector3 colLargest = v.GetColumn(largestComponentIndex); Vector3 col1 = v.GetColumn(1); v.SetColumn(1, colLargest); v.SetColumn(largestComponentIndex, col1); v.SetColumn(2, Vector3.Cross(v.GetColumn(0), v.GetColumn(1))); } // Compute capsule for the orientation given by v. Vector3 center; ComputeBoundingCapsule(points, v, out radius, out height, out center); pose = new Pose(center, v); }
public static void ComputeBoundingBox(IList <Vector3> points, out Vector3 extent, out Pose pose) { // PCA of the convex hull is used (see "Physics-Based Animation", pp. 483, and others.) // in addition to a brute force search. The optimum is returned. if (points == null) { throw new ArgumentNullException("points"); } if (points.Count == 0) { throw new ArgumentException("The list of 'points' is empty."); } // Covariance matrix. MatrixF cov = null; // ReSharper disable EmptyGeneralCatchClause try { if (points.Count > 4) { // Reduce point list to convex hull. DcelMesh dcelMesh = CreateConvexHull(points); TriangleMesh mesh = dcelMesh.ToTriangleMesh(); // Use reduced point list - if we have found a useful one. (Line objects do not have // a useful triangle mesh.) if (mesh.Vertices.Count > 0) { points = mesh.Vertices; cov = ComputeCovarianceMatrixFromSurface(mesh); } } } catch { } // ReSharper restore EmptyGeneralCatchClause // If anything happens in the convex hull creation, we can still go on including the // interior points and compute the covariance matrix for the points instead of the // surface. if (cov == null || Numeric.IsNaN(cov.Determinant())) { // Make copy of points list because ComputeBoundingBox() will reorder the points. points = points.ToList(); cov = ComputeCovarianceMatrixFromPoints(points); } // Perform Eigenvalue decomposition. EigenvalueDecompositionF evd = new EigenvalueDecompositionF(cov); // v transforms from local coordinate space of the box into world space. var v = evd.V.ToMatrix(); Debug.Assert(v.GetColumn(0).IsNumericallyNormalized); Debug.Assert(v.GetColumn(1).IsNumericallyNormalized); Debug.Assert(v.GetColumn(2).IsNumericallyNormalized); // v is like a rotation matrix, but the coordinate system is not necessarily right handed. // --> Make sure it is right-handed. v.SetColumn(2, Vector3.Cross(v.GetColumn(0), v.GetColumn(1))); // Another way to do this: //// Make sure that V is a rotation matrix. (V could be an orthogonal matrix that //// contains a mirror operation. In other words, V could be a rotation matrix for //// a left handed coordinate system.) //if (!v.IsRotation) //{ // // Swap two columns to create a right handed rotation matrix. // Vector3 col1 = v.GetColumn(2); // Vector3 col2 = v.GetColumn(2); // v.SetColumn(1, col2); // v.SetColumn(2, col1); //} // If the box axes are parallel to the world axes, create a box with NO rotation. TryToMakeIdentityMatrix(ref v); Vector3 center; float volume = ComputeBoundingBox(points, v, float.PositiveInfinity, out extent, out center); // Brute force search for better box. // This was inspired by the OBB algorithm of John Ratcliff, www.codesuppository.com. Vector3 αBest = Vector3.Zero; // Search for optimal angles. float αMax = MathHelper.ToRadians(45); // On each axis we rotate from -αMax to +αMax. float αMin = MathHelper.ToRadians(1); // We abort when αMax == 1°. const float numberOfSteps = 7; // In each iteration we divide αMax in this number of steps. // In each loop we test angles between -αMax and +αMax. // Then we half αMax and search again. while (αMax >= αMin) { bool foundBetterAngles = false; // Better angles found? float αStep = αMax / numberOfSteps; // We test around this angle: Vector3 α = αBest; for (float αX = α.X - αMax; αX <= α.X + αMax; αX += αStep) { for (float αY = α.Y - αMax; αY <= α.Y + αMax; αY += αStep) { for (float αZ = α.Z - αMax; αZ <= α.Z + αMax; αZ += αStep) { Vector3 centerNew; Vector3 boxExtentNew; Matrix vNew = Quaternion.CreateFromRotationMatrix(αX, Vector3.UnitX, αY, Vector3.UnitY, αZ, Vector3.UnitZ, true).ToRotationMatrix33(); float volumeNew = ComputeBoundingBox(points, vNew, volume, out boxExtentNew, out centerNew); if (volumeNew < volume) { foundBetterAngles = true; center = centerNew; extent = boxExtentNew; v = vNew; volume = volumeNew; αBest = new Vector3(αX, αY, αZ); } } } } // Search again in half the interval around the best angles or abort. if (foundBetterAngles) { αMax *= 0.5f; } else { αMax = 0; } } pose = new Pose(center, v); }
/// <summary> /// Called when a mesh should be generated for the shape. /// </summary> /// <param name="absoluteDistanceThreshold">The absolute distance threshold.</param> /// <param name="iterationLimit">The iteration limit.</param> /// <returns>The triangle mesh for this shape.</returns> protected override TriangleMesh OnGetMesh(float absoluteDistanceThreshold, int iterationLimit) { DcelMesh mesh = GeometryHelper.CreateConvexHull(Points); return(mesh.ToTriangleMesh()); }
private void BuildVertexAdjacencyLists(DcelMesh convexHull) { _vertexAdjacency = new VertexAdjacency(convexHull); }
public static DcelMesh ToDCEL(Mesh mesh) { var dcel = new DcelMesh(); var vertices = new HVertex[mesh.vertices.Count]; var faces = new Face[mesh.triangles.Count]; dcel.HalfEdges.Capacity = 2 * mesh.NumberOfEdges; mesh.Renumber(); HVertex vertex; foreach (var v in mesh.vertices.Values) { vertex = new HVertex(v.x, v.y); vertex.id = v.id; vertex.label = v.label; vertices[v.id] = vertex; } // Maps a triangle to its 3 edges (used to set next pointers). var map = new List <HalfEdge> [mesh.triangles.Count]; Face face; foreach (var t in mesh.triangles) { face = new Face(null); face.id = t.id; faces[t.id] = face; map[t.id] = new List <HalfEdge>(3); } Otri tri = default(Otri), neighbor = default(Otri); TriangleNet.Geometry.Vertex org, dest; int id, nid, count = mesh.triangles.Count; HalfEdge edge, twin, next; var edges = dcel.HalfEdges; // Count half-edges (edge ids). int k = 0; // Maps a vertex to its leaving boundary edge. var boundary = new Dictionary <int, HalfEdge>(); foreach (var t in mesh.triangles) { id = t.id; tri.tri = t; for (int i = 0; i < 3; i++) { tri.orient = i; tri.Sym(ref neighbor); nid = neighbor.tri.id; if (id < nid || nid < 0) { face = faces[id]; // Get the endpoints of the current triangle edge. org = tri.Org(); dest = tri.Dest(); // Create half-edges. edge = new HalfEdge(vertices[org.id], face); twin = new HalfEdge(vertices[dest.id], nid < 0 ? Face.Empty : faces[nid]); map[id].Add(edge); if (nid >= 0) { map[nid].Add(twin); } else { boundary.Add(dest.id, twin); } // Set leaving edges. edge.origin.leaving = edge; twin.origin.leaving = twin; // Set twin edges. edge.twin = twin; twin.twin = edge; edge.id = k++; twin.id = k++; edges.Add(edge); edges.Add(twin); } } } // Set next pointers for each triangle face. foreach (var t in map) { edge = t[0]; next = t[1]; if (edge.twin.origin.id == next.origin.id) { edge.next = next; next.next = t[2]; t[2].next = edge; } else { edge.next = t[2]; next.next = edge; t[2].next = next; } } // Resolve boundary edges. foreach (var e in boundary.Values) { e.next = boundary[e.twin.origin.id]; } dcel.Vertices.AddRange(vertices); dcel.Faces.AddRange(faces); return(dcel); }