/// <summary> /// Returns the incircle of a triangular face. /// Assumes face is triangular. /// </summary> /// <returns></returns> public static Circle GetIncircle <V, E, F>(this HeStructure <V, E, F> .Face face, Func <V, Vector3d> getPosition) where V : HeStructure <V, E, F> .Vertex where E : HeStructure <V, E, F> .Halfedge where F : HeStructure <V, E, F> .Face { // impl ref // http://mathworld.wolfram.com/Incenter.html var he = face.First; var p0 = getPosition(he.Previous.Start); var p1 = getPosition(he.Start); var p2 = getPosition(he.Next.Start); var d01 = p0.DistanceTo(p1); var d12 = p1.DistanceTo(p2); var d20 = p2.DistanceTo(p0); var p = (d01 + d12 + d20) * 0.5; // semiperimeter var pInv = 1.0 / p; // inverse semiperimeter var radius = Math.Sqrt(p * (p - d01) * (p - d12) * (p - d20)) * pInv; // triangle area (Heron's formula) / semiperimeter pInv *= 0.5; // inverse perimeter var center = p0 * (d12 * pInv) + p1 * (d20 * pInv) + p2 * (d01 * pInv); return(new Circle(new Plane(p0, p1, (Point3d)p2), center, radius)); }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="K"></typeparam> /// <param name="start"></param> /// <param name="getKey"></param> /// <returns></returns> public static IEnumerable <E> WalkToMax <V, E, K>(this HeStructure <V, E> .Halfedge start, Func <E, K> getKey) where V : HeStructure <V, E> .Vertex where E : HeStructure <V, E> .Halfedge where K : IComparable <K> { return(GraphSearch.WalkToMax(start.Self, GetConnected <V, E>, getKey)); }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="structure"></param> /// <param name="getPosition"></param> /// <param name="setResult"></param> /// <param name="parallel"></param> public static void GetFacePolylines <V, E, F>(this HeStructure <V, E, F> structure, Func <V, Vec3d> getPosition, Action <F, Polyline> setResult, bool parallel = false) where V : HeVertex <V, E, F> where E : Halfedge <V, E, F> where F : HeFace <V, E, F> { var faces = structure.Faces; if (parallel) { Parallel.ForEach(Partitioner.Create(0, faces.Count), range => Body(range.Item1, range.Item2)); } else { Body(0, faces.Count); } void Body(int from, int to) { for (int i = from; i < to; i++) { var f = faces[i]; if (f.IsUnused) { continue; } setResult(f, f.ToPolyline(getPosition)); } } }
/// <summary> /// Returns the incircle of a triangular face. /// Assumes face is triangular. /// http://mathworld.wolfram.com/Incenter.html /// </summary> /// <returns></returns> public static Circle GetIncircle <V, E, F>(this HeStructure <V, E, F> .Face face) where V : HeStructure <V, E, F> .Vertex, IPosition3d where E : HeStructure <V, E, F> .Halfedge where F : HeStructure <V, E, F> .Face { return(GetIncircle(face, Position3d <V> .Get)); }
/// <summary> /// Returns the entries of the Laplacian matrix. /// </summary> public static void GetVertexLaplacian <V, E>(this HeStructure <V, E> graph, double[] result) where V : HeStructure <V, E> .Vertex where E : HeStructure <V, E> .Halfedge { var verts = graph.Vertices; int nv = verts.Count; Array.Clear(result, 0, nv * nv); for (int i = 0; i < nv; i++) { var v = verts[i]; if (v.IsUnused) { continue; } double wsum = 0.0; int offset = i * nv; foreach (var he in v.OutgoingHalfedges) { result[offset + he.End.Index] = 1.0; wsum++; } result[offset + i] = -wsum; } }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <param name="structure"></param> /// <param name="getPosition"></param> /// <param name="setResult"></param> /// <param name="parallel"></param> public static void GetEdgeLines <V, E>(this HeStructure <V, E> structure, Func <V, Vec3d> getPosition, Action <E, Line> setResult, bool parallel = false) where V : HeVertex <V, E> where E : Halfedge <V, E> { var edges = structure.Edges; if (parallel) { Parallel.ForEach(Partitioner.Create(0, edges.Count), range => Body(range.Item1, range.Item2)); } else { Body(0, edges.Count); } void Body(int from, int to) { for (int i = from; i < to; i++) { var he = edges[i]; if (he.IsUnused) { continue; } setResult(he, he.ToLine(getPosition)); } } }
/// <summary> /// Returns the entries of the adjacency matrix. /// </summary> /// <returns></returns> public static void GetFaceAdjacency <V, E, F>(this HeStructure <V, E, F> mesh, double[] result) where V : HeStructure <V, E, F> .Vertex where E : HeStructure <V, E, F> .Halfedge where F : HeStructure <V, E, F> .Face { var faces = mesh.Faces; int nf = faces.Count; Array.Clear(result, 0, nf * nf); for (int i = 0; i < nf; i++) { var f0 = faces[i]; if (f0.IsUnused) { continue; } var offset = i * nf; foreach (var f1 in f0.AdjacentFaces) { result[offset + f1.Index] = 1.0; } } }
/// <summary> /// Returns the entries of the Laplacian matrix. /// </summary> public static void GetFaceLaplacian <V, E, F>(this HeStructure <V, E, F> mesh, Func <E, double> getWeight, double[] result) where V : HeStructure <V, E, F> .Vertex where E : HeStructure <V, E, F> .Halfedge where F : HeStructure <V, E, F> .Face { var faces = mesh.Faces; int nf = faces.Count; Array.Clear(result, 0, nf * nf); for (int i = 0; i < nf; i++) { var f = faces[i]; if (f.IsUnused) { continue; } double wsum = 0.0; var offset = i * nf; foreach (var he in f.Halfedges) { double w = getWeight(he); result[offset + he.End.Index] = w; wsum += w; } result[offset + i] = -wsum; } }
/// <summary> /// Returns the entries of the incidence matrix in row-major order. /// </summary> /// <returns></returns> public static void GetFaceIncidence <V, E, F>(this HeStructure <V, E, F> mesh, double[] result) where V : HeStructure <V, E, F> .Vertex where E : HeStructure <V, E, F> .Halfedge where F : HeStructure <V, E, F> .Face { var faces = mesh.Faces; int nf = faces.Count; int ne = mesh.Edges.Count; Array.Clear(result, 0, nf * ne); for (int i = 0; i < nf; i++) { var f = faces[i]; if (f.IsUnused) { continue; } int offset = i * ne; foreach (var he in f.Halfedges) { result[offset + he.EdgeIndex] = 1.0; } } }
/// <summary> /// Returns the entries of the incidence matrix in row-major order. /// </summary> /// <returns></returns> public static void GetVertexIncidence <V, E>(this HeStructure <V, E> graph, double[] result) where V : HeStructure <V, E> .Vertex where E : HeStructure <V, E> .Halfedge { var verts = graph.Vertices; int nv = verts.Count; int ne = graph.Edges.Count; Array.Clear(result, 0, nv * ne); for (int i = 0; i < nv; i++) { var v = verts[i]; if (v.IsUnused) { continue; } int offset = i * ne; foreach (var he in v.OutgoingHalfedges) { result[offset + he.EdgeIndex] = 1.0; } } }
/// <summary> /// Returns the entries of the Laplacian matrix. /// </summary> private static void GetVertexLaplacianSymmetric <V, E>(this HeStructure <V, E> graph, Func <E, double> getWeight, Func <V, double> getMass, double[] result) where V : HeStructure <V, E> .Vertex where E : HeStructure <V, E> .Halfedge { // impl ref // http://reuter.mit.edu/papers/reuter-smi09.pdf var verts = graph.Vertices; int nv = verts.Count; Array.Clear(result, 0, nv * nv); for (int i = 0; i < nv; i++) { var v0 = verts[i]; if (v0.IsUnused) { continue; } var a0 = getMass(v0); double wsum = 0.0; int offset = i * nv; foreach (var he in v0.OutgoingHalfedges) { double w = getWeight(he) / Math.Sqrt(a0 * getMass(he.End)); result[offset + he.End.Index] = w; wsum += w; } result[offset + i] = -wsum; } }
/// <summary> /// Returns the entries of the Laplacian matrix in row-major order. /// </summary> private static void GetVertexLaplacian <V, E>(this HeStructure <V, E> graph, Func <E, double> getWeight, Func <V, double> getMass, double[] result) where V : HeStructure <V, E> .Vertex where E : HeStructure <V, E> .Halfedge { var verts = graph.Vertices; int nv = verts.Count; Array.Clear(result, 0, nv * nv); for (int i = 0; i < nv; i++) { var v = verts[i]; if (v.IsUnused) { continue; } var aInv = 1.0 / getMass(v); double wsum = 0.0; int offset = i * nv; foreach (var he in v.OutgoingHalfedges) { double w = getWeight(he) * aInv; result[offset + he.End.Index] = w; wsum += w; } result[offset + i] = -wsum; } }
/// <summary> /// /// </summary> /// <returns></returns> public static double GetArea <V, E, F>(this HeStructure <V, E, F> .Face face) where V : HeStructure <V, E, F> .Vertex, IPosition3d where E : HeStructure <V, E, F> .Halfedge where F : HeStructure <V, E, F> .Face { return(GetArea(face, Delegates.Position3d <V> .Get)); }
/// <summary> /// Calcuated as the signed angle between adjacent face normals where convex is positive. /// Assumes the given face normals are unitized. /// </summary>> public static double GetDihedralAngle <V, E, F>(this HeStructure <V, E, F> .Halfedge hedge, Func <F, Vector3d> getNormal) where V : HeStructure <V, E, F> .Vertex, IPosition3d where E : HeStructure <V, E, F> .Halfedge where F : HeStructure <V, E, F> .Face { return(GetDihedralAngle(hedge, Position3d <V> .Get, getNormal)); }
/// <summary> /// Returns the circumcenter of the face. /// Assumes face is triangular. /// http://mathworld.wolfram.com/Incenter.html /// </summary> /// <returns></returns> public static Vector3d GetIncenter <V, E, F>(this HeStructure <V, E, F> .Face face) where V : HeStructure <V, E, F> .Vertex, IPosition3d where E : HeStructure <V, E, F> .Halfedge where F : HeStructure <V, E, F> .Face { return(GetIncenter(face, Delegates.Position3d <V> .Get)); }
/// <summary> /// Returns the area of the given halfedge as per D in https://www.cs.cmu.edu/~kmcrane/Projects/Other/TriangleAreasCheatSheet.pdf /// Assumes adjacent faces are triangular. /// </summary> public static double GetEdgeArea <V, E, F>(this HeStructure <V, E, F> .Halfedge hedge) where V : HeStructure <V, E, F> .Vertex, IPosition3d where E : HeStructure <V, E, F> .Halfedge where F : HeStructure <V, E, F> .Face { return(GetEdgeArea(hedge, Position3d <V> .Get)); }
/// <summary> /// Returns the average position of vertices in the face. /// </summary> /// <returns></returns> public static Vector3d GetBarycenter <V, E, F>(this HeStructure <V, E, F> .Face face, Func <V, Vector3d> getPosition) where V : HeStructure <V, E, F> .Vertex where E : HeStructure <V, E, F> .Halfedge where F : HeStructure <V, E, F> .Face { return(face.Vertices.Mean(getPosition)); }
/// <summary> /// Calculates the unit length area-weighted edge normal. /// </summary> public static Vector3d GetEdgeNormal <V, E, F>(this HeStructure <V, E, F> .Halfedge hedge) where V : HeStructure <V, E, F> .Vertex, IPosition3d where E : HeStructure <V, E, F> .Halfedge where F : HeStructure <V, E, F> .Face { return(GetEdgeNormal(hedge, Position3d <V> .Get)); }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="K"></typeparam> /// <param name="start"></param> /// <param name="getKey"></param> /// <returns></returns> public static E NearestMin <V, E, K>(this HeStructure <V, E> .Halfedge start, Func <E, K> getKey) where V : HeStructure <V, E> .Vertex where E : HeStructure <V, E> .Halfedge where K : IComparable <K> { return(GraphSearch.NearestMin(start.Self, GetConnected <V, E>, getKey)); }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="K"></typeparam> /// <param name="start"></param> /// <param name="getKey"></param> /// <returns></returns> public static V NearestMax <V, E, K>(this HeStructure <V, E> .Vertex start, Func <V, K> getKey) where V : HeStructure <V, E> .Vertex where E : HeStructure <V, E> .Halfedge where K : IComparable <K> { return(GraphSearch.NearestMax(start.Self, v => v.ConnectedVertices, getKey)); }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="K"></typeparam> /// <param name="start"></param> /// <param name="getKey"></param> /// <returns></returns> public static IEnumerable <V> WalkToMin <V, E, K>(this HeStructure <V, E> .Vertex start, Func <V, K> getKey) where V : HeStructure <V, E> .Vertex where E : HeStructure <V, E> .Halfedge where K : IComparable <K> { return(GraphSearch.WalkToMin(start.Self, v => v.ConnectedVertices, getKey)); }
/// <summary> /// Calculates the discrete mean curvature over the area of the given vertex. /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="vertex"></param> /// <param name="getWeight"></param> /// <returns></returns> public static double GetMeanCurvature <V, E, F>(this HeStructure <V, E, F> .Vertex vertex, Func <E, double> getWeight) where V : HeStructure <V, E, F> .Vertex, IPosition3d where E : HeStructure <V, E, F> .Halfedge where F : HeStructure <V, E, F> .Face { return(GetMeanCurvature(vertex, Position3d <V> .Get, getWeight)); }
/// <summary> /// Calculates the circle packing radii for the given vertex. /// Assumes the mesh is a circle packing (CP) mesh as defined in http://www.geometrie.tuwien.ac.at/hoebinger/mhoebinger_files/circlepackings.pdf /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <returns></returns> public static double GetCirclePackingRadius <V, E, F>(this HeStructure <V, E, F> .Vertex vertex, Func <V, Vector3d> getPosition) where V : HeStructure <V, E, F> .Vertex where E : HeStructure <V, E, F> .Halfedge where F : HeStructure <V, E, F> .Face { var p0 = getPosition(vertex.Self); var sum = 0.0; var n = 0; foreach (var he0 in vertex.OutgoingHalfedges) { if (he0.Face == null) { continue; } var he1 = he0.Next; var p1 = getPosition(he1.Start); var p2 = getPosition(he1.End); sum += p0.DistanceTo(p1) - p1.DistanceTo(p2) * 0.5; n++; } return(sum / n); }
/// <summary> /// Calculates the angle defect at the given vertex. /// This is also a measure of discrete Gaussian curvature over the area of the given vertex. /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <returns></returns> public static double GetAngleDefect <V, E, F>(this HeStructure <V, E, F> .Vertex vertex) where V : HeStructure <V, E, F> .Vertex, IPosition3d where E : HeStructure <V, E, F> .Halfedge where F : HeStructure <V, E, F> .Face { return(GetAngleDefect(vertex, Position3d <V> .Get)); }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="vertex"></param> /// <param name="getPosition"></param> /// <param name="getWeight"></param> /// <returns></returns> public static Vector3d GetNormal <V, E, F>(this HeStructure <V, E, F> .Vertex vertex, Func <V, Vector3d> getPosition, Func <E, double> getWeight) where V : HeStructure <V, E, F> .Vertex where E : HeStructure <V, E, F> .Halfedge where F : HeStructure <V, E, F> .Face { return(vertex.OutgoingHalfedges.WeightedSum(he => he.GetNormal(getPosition), getWeight).Unit); }
/// <summary> /// Returns the unitized sum of area-weighted halfedge normals around the vertex. /// </summary> /// <returns></returns> public static Vector3d GetNormal <V, E, F>(this HeStructure <V, E, F> .Vertex vertex, Func <V, Vector3d> getPosition) where V : HeStructure <V, E, F> .Vertex where E : HeStructure <V, E, F> .Halfedge where F : HeStructure <V, E, F> .Face { return(vertex.OutgoingHalfedges.Where(he => !he.IsHole).Sum(he => he.GetNormal(getPosition)).Unit); }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="face"></param> /// <returns></returns> public static Polyline ToPolyline <V, E, F>(this HeStructure <V, E, F> .Face face) where V : HeStructure <V, E, F> .Vertex, IPosition3d where E : HeStructure <V, E, F> .Halfedge where F : HeStructure <V, E, F> .Face { return(ToPolyline(face, Position3d <V> .Get)); }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <typeparam name="K"></typeparam> /// <param name="start"></param> /// <param name="getKey"></param> /// <returns></returns> public static IEnumerable <F> WalkToMax <V, E, F, K>(this HeStructure <V, E, F> .Face start, Func <F, K> getKey) where V : HeStructure <V, E, F> .Vertex where E : HeStructure <V, E, F> .Halfedge where F : HeStructure <V, E, F> .Face where K : IComparable <K> { return(GraphSearch.WalkToMax(start.Self, f => f.AdjacentFaces, getKey)); }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <typeparam name="K"></typeparam> /// <param name="start"></param> /// <param name="getKey"></param> /// <returns></returns> public static F NearestMin <V, E, F, K>(this HeStructure <V, E, F> .Face start, Func <F, K> getKey) where V : HeStructure <V, E, F> .Vertex where E : HeStructure <V, E, F> .Halfedge where F : HeStructure <V, E, F> .Face where K : IComparable <K> { return(GraphSearch.NearestMin(start.Self, f => f.AdjacentFaces, getKey)); }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <param name="hedge"></param> /// <param name="getPosition"></param> /// <returns></returns> public static Line ToLine <V, E>(this HeStructure <V, E> .Halfedge hedge, Func <V, Vector3d> getPosition) where V : HeStructure <V, E> .Vertex where E : HeStructure <V, E> .Halfedge { var p0 = getPosition(hedge.Start); var p1 = getPosition(hedge.End); return(new Line(p0.X, p0.Y, p0.Z, p1.X, p1.Y, p1.Z)); }