/// <summary> /// /// </summary> /// <param name="path"></param> public static void WriteToObj <TV, TE, TF>(HeMesh <TV, TE, TF> mesh, string path, Func <TV, Vec2d> getTexture = null) where TV : HeMesh <TV, TE, TF> .Vertex, IPosition3d, INormal3d where TE : HeMesh <TV, TE, TF> .Halfedge where TF : HeMesh <TV, TE, TF> .Face { WriteToObj(mesh, path, IPosition3d <TV> .Get, INormal3d <TV> .Get, getTexture); }
/// <summary> /// /// </summary> /// <typeparam name="UV"></typeparam> /// <typeparam name="UE"></typeparam> /// <typeparam name="UF"></typeparam> /// <param name="mesh"></param> /// <param name="setVertex"></param> /// <param name="setHedge"></param> /// <param name="setFace"></param> /// <returns></returns> public TM[] CreateConnectedComponents <UV, UE, UF>(HeMesh <UV, UE, UF> mesh, Action <TV, UV> setVertex = null, Action <TE, UE> setHedge = null, Action <TF, UF> setFace = null) where UV : HeMesh <UV, UE, UF> .Vertex where UE : HeMesh <UV, UE, UF> .Halfedge where UF : HeMesh <UV, UE, UF> .Face { return(CreateConnectedComponents(mesh, out int[] compIds, out int[] edgeIds, setVertex, setHedge, setFace)); }
/// <summary> /// /// </summary> /// <param name="mesh"></param> /// <returns></returns> public G CreateFromFaceTopology(HeMesh <HeMesh3d.Vertex, HeMesh3d.Halfedge, HeMesh3d.Face> mesh) { var graph = Create(mesh.Faces.Count, mesh.Halfedges.Count); graph.AppendFaceTopology(mesh); return(graph); }
/// <summary> /// /// </summary> private static void CatmullClarkSmoothFixed <V, E, F>(HeMesh <V, E, F> mesh, Property <V, Vec3d> position) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { var verts = mesh.Vertices; int ev0 = verts.Count - mesh.Edges.Count; // index of first edge vertex int fv0 = ev0 - mesh.Faces.Count; // index of first face vertex // set old vertices for (int i = 0; i < fv0; i++) { var v = verts[i]; if (v.IsUnused || v.IsBoundary) { continue; // skip boundary verts } var fsum = new Vec3d(); var esum = new Vec3d(); int n = 0; foreach (var he in v.OutgoingHalfedges) { fsum += position.Get(verts[he.Face.Index + fv0]); esum += position.Get(verts[(he.Index >> 1) + ev0]); n++; } var t = 1.0 / n; position.Set(v, (position.Get(v) * (n - 3) + fsum * t + 2 * esum * t) * t); } }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="mesh"></param> /// <param name="position"></param> private static void QuadSplitGeometry <V, E, F>(HeMesh <V, E, F> mesh, Property <V, Vec3d> position) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { // create face vertices foreach (var f in mesh.Faces) { var v = mesh.AddVertex(); if (!f.IsUnused) { position.Set(v, f.Vertices.Mean(position.Get)); } } // create edge vertices foreach (var he in mesh.Edges) { var v = mesh.AddVertex(); if (!he.IsUnused) { var p = (position.Get(he.Start) + position.Get(he.End)) * 0.5; position.Set(v, p); } } }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="mesh"></param> /// <returns></returns> public static Strip <V, E, F> CreateStrip <V, E, F>(HeMesh <V, E, F> mesh) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { return(new Strip <V, E, F>(mesh, f => f.First)); }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="mesh"></param> /// <param name="getStart"></param> /// <returns></returns> public static Strip <V, E, F> CreateStrip <V, E, F>(HeMesh <V, E, F> mesh, Func <F, E> getStart) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { return(new Strip <V, E, F>(mesh, getStart)); }
/// <summary> /// /// </summary> /// <param name="mesh"></param> /// <param name="start"></param> /// <param name="setEdge"></param> public static void DetachFaceCycles(HeMesh <V, E, F> mesh, F start, Action <E, E> setEdge) { var currTag = mesh.Halfedges.NextTag; // tag traversed edges during BFS foreach (var he in mesh.GetFacesBreadthFirst2(start.Yield())) { he.Edge.Tag = currTag; } var edges = mesh.Edges; var ne = edges.Count; // detach all untagged edges for (int i = 0; i < ne; i++) { var he0 = edges[i]; if (he0.IsUnused || he0.IsBoundary || he0.Tag == currTag) { continue; } var he1 = mesh.DetachEdgeImpl(he0); setEdge(he1, he0); } }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="mesh"></param> /// <param name="start"></param> /// <param name="setEdge"></param> public static void DetachFaceCycles <V, E, F>(HeMesh <V, E, F> mesh, F start, Action <E, E> setEdge) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { Impl <V, E, F> .DetachFaceCycles(mesh, start, setEdge); }
/// <summary> /// Assumes quadrilateral faces. /// </summary> public static IEnumerable <HeQuadStrip <V, E, F> > GetQuadStrips <V, E, F>(HeMesh <V, E, F> mesh, bool flip) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { var faces = mesh.Faces; var result = new List <HeQuadStrip <V, E, F> >(); var stack = new Stack <E>(); int currTag = faces.NextTag; for (int i = 0; i < faces.Count; i++) { var f = faces[i]; if (f.IsUnused || f.Tag == currTag || !f.IsDegree(4)) { continue; // skip if unused, visited, or non-quads } stack.Push((flip) ? f.First.Next : f.First); foreach (var strip in GetQuadStrips <V, E, F>(stack, currTag)) { yield return(strip); } } }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="mesh"></param> /// <param name="start"></param> /// <param name="setEdge"></param> public static void Unroll <V, E, F>(HeMesh <V, E, F> mesh, F start, Func <V, Vec3d> getPosition, Action <V, Vec3d> setUnrolledPosition, Func <E, double> getUnrollFactor = null) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { Impl <V, E, F> .Unroll(mesh, start, getPosition, setUnrolledPosition, getUnrollFactor); }
/// <summary> /// Throws an exception if the topology of the given mesh is not valid. /// </summary> /// <typeparam name="TV"></typeparam> /// <typeparam name="TE"></typeparam> /// <typeparam name="TF"></typeparam> /// <param name="mesh"></param> internal static void CheckTopology <TV, TE, TF>(HeMesh <TV, TE, TF> mesh) where TV : HeMesh <TV, TE, TF> .Vertex where TE : HeMesh <TV, TE, TF> .Halfedge where TF : HeMesh <TV, TE, TF> .Face { var verts = mesh.Vertices; var hedges = mesh.Halfedges; var faces = mesh.Faces; // ensure halfedges are reciprocally linked foreach (var he in hedges) { if (he.IsUnused) { continue; } if (he.Previous.Next != he && he.Next.Previous != he) { Throw(); } if (he.Start.IsUnused) { Throw(); } if (he.Face.IsUnused) { Throw(); } } // ensure consistent start vertex during circulation foreach (var v in verts) { foreach (var he in v.OutgoingHalfedges) { if (he.Start != v) { Throw(); } } } // ensure consistent face during circulation foreach (var f in faces) { foreach (var he in f.Halfedges) { if (he.Face != f) { Throw(); } } } void Throw() { throw new Exception("The topology of the given mesh is invalid"); } }
/// <summary> /// Applies a single iteration of Catmull-Clark subdivision to the given mesh. /// If using external buffers to store vertex attributes, the number of vertices after subdivision equals the sum of the number of vertices, edges, and faces in the initial mesh. /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="mesh"></param> /// <param name="position"></param> public static void QuadSplit <V, E, F>(HeMesh <V, E, F> mesh, Property <V, Vec3d> position) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { QuadSplitGeometry(mesh, position); QuadSplitTopology(mesh); }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="mesh"></param> /// <param name="position"></param> public static void Loop <V, E, F>(HeMesh <V, E, F> mesh, Property <V, Vec3d> position) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { // TODO implement throw new NotImplementedException(); }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="mesh"></param> /// <param name="hedge"></param> /// <returns></returns> public static HeQuadStrip <V, E, F> GetQuadStrip <V, E, F>(HeMesh <V, E, F> mesh, E hedge) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { mesh.Halfedges.OwnsCheck(hedge); return(GetQuadStrip <V, E, F>(hedge, mesh.Faces.NextTag)); }
/// <summary> /// Starts the triangulation from the halfedge with the smallest key in each face. /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="mesh"></param> /// <param name="getKey"></param> /// <returns></returns> public static Fan <V, E, F> CreateFan <V, E, F, K>(HeMesh <V, E, F> mesh, Func <E, K> getKey) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face where K : IComparable <K> { return(new Fan <V, E, F>(mesh, f => f.Halfedges.SelectMin(getKey))); }
/// <summary> /// /// </summary> /// <typeparam name="UV"></typeparam> /// <typeparam name="UE"></typeparam> /// <typeparam name="UF"></typeparam> /// <param name="mesh"></param> /// <param name="setVertex"></param> /// <param name="setHedge"></param> public void AppendVertexTopology <UV, UE, UF>(HeMesh <UV, UE, UF> mesh, Action <TV, UV> setVertex = null, Action <TE, UE> setHedge = null) where UV : HeMesh <UV, UE, UF> .Vertex where UE : HeMesh <UV, UE, UF> .Halfedge where UF : HeMesh <UV, UE, UF> .Face { int nhe = Halfedges.Count; int nv = Vertices.Count; var meshHedges = mesh.Halfedges; var meshVerts = mesh.Vertices; // append new elements for (int i = 0; i < meshVerts.Count; i++) { AddVertex(); } for (int i = 0; i < meshHedges.Count; i += 2) { AddEdge(); } // set vertex refs for (int i = 0; i < meshVerts.Count; i++) { var v0 = meshVerts[i]; var v1 = Vertices[i + nv]; // transfer attributes setVertex?.Invoke(v1, v0); if (v0.IsUnused) { continue; } v1.First = Halfedges[v0.First + nhe]; } // set halfedge refs for (int i = 0; i < meshHedges.Count; i++) { var he0 = meshHedges[i]; var he1 = Halfedges[i + nhe]; // transfer attributes setHedge?.Invoke(he1, he0); if (he0.IsUnused) { continue; } he1.Previous = Halfedges[he0.Previous + nhe]; he1.Next = Halfedges[he0.Next + nhe]; he1.Start = Vertices[he0.Start + nv]; } }
/// <summary> /// Returns the dual of the given mesh. /// Action delegates specify how attributes of primal elements are mapped to attributes of dual elements. /// Note this method preserves indexical correspondance between primal and dual elements. /// </summary> /// <typeparam name="UV"></typeparam> /// <typeparam name="UE"></typeparam> /// <typeparam name="UF"></typeparam> /// <param name="mesh"></param> /// <param name="setVertex"></param> /// <param name="setHedge"></param> /// <param name="setFace"></param> /// <returns></returns> public TM CreateDual <UV, UE, UF>(HeMesh <UV, UE, UF> mesh, Action <TV, UF> setVertex = null, Action <TE, UE> setHedge = null, Action <TF, UV> setFace = null) where UV : HeMesh <UV, UE, UF> .Vertex where UE : HeMesh <UV, UE, UF> .Halfedge where UF : HeMesh <UV, UE, UF> .Face { var dual = Create(mesh.Vertices.Capacity, mesh.Halfedges.Capacity, mesh.Faces.Capacity); dual.AppendDual(mesh, setVertex, setHedge, setFace); return(dual); }
/// <summary> /// /// </summary> /// <typeparam name="UV"></typeparam> /// <typeparam name="UE"></typeparam> /// <typeparam name="UF"></typeparam> /// <param name="mesh"></param> /// <param name="setHedge"></param> /// <param name="setVertex"></param> /// <returns></returns> public TG CreateFromFaceTopology <UV, UE, UF>(HeMesh <UV, UE, UF> mesh, Action <TV, UF> setVertex = null, Action <TE, UE> setHedge = null) where UV : HeMesh <UV, UE, UF> .Vertex where UE : HeMesh <UV, UE, UF> .Halfedge where UF : HeMesh <UV, UE, UF> .Face { var result = Create(mesh.Faces.Count, mesh.Halfedges.Count); result.AppendFaceTopology(mesh, setVertex, setHedge); return(result); }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> public static void WriteToJson <V, E, F, VA, EA, FA>(HeMesh <V, E, F> mesh, string path, Func <V, VA> getVertexAttributes = null, Func <E, EA> getHedgeAttributes = null, Func <F, FA> getFaceAttributes = null) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { var buffer = new HeMeshJsonBuffer <VA, EA, FA>(); buffer.WriteFrom(mesh, getVertexAttributes, getHedgeAttributes, getFaceAttributes); CoreIO.SerializeJson(buffer, path); }
/// <summary> /// Action delegates specify how attributes of original elements are mapped to attributes of copied elements. /// </summary> /// <typeparam name="UV"></typeparam> /// <typeparam name="UE"></typeparam> /// <typeparam name="UF"></typeparam> /// <param name="mesh"></param> /// <param name="setVertex"></param> /// <param name="setHedge"></param> /// <param name="setFace"></param> /// <returns></returns> public TM CreateCopy <UV, UE, UF>(HeMesh <UV, UE, UF> mesh, Action <TV, UV> setVertex = null, Action <TE, UE> setHedge = null, Action <TF, UF> setFace = null) where UV : HeMesh <UV, UE, UF> .Vertex where UE : HeMesh <UV, UE, UF> .Halfedge where UF : HeMesh <UV, UE, UF> .Face { var copy = Create(mesh.Vertices.Capacity, mesh.Halfedges.Capacity, mesh.Faces.Capacity); copy.Append(mesh, setVertex, setHedge, setFace); return(copy); }
/// <summary> /// /// </summary> /// <param name="path"></param> /// <param name="graph"></param> /// <param name="setVertexAttributes"></param> /// <param name="setHedgeAttributes"></param> public static void ReadFromJson <V, E, F, VA, EA, FA>(string path, HeMesh <V, E, F> mesh, Action <V, VA> setVertexAttributes = null, Action <E, EA> setHedgeAttributes = null, Action <F, FA> setFaceAttributes = null) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { var buffer = CoreIO.DeserializeJson <HeMeshJsonBuffer <VA, EA, FA> >(path); buffer.ReadTo(mesh, setVertexAttributes, setHedgeAttributes, setFaceAttributes); HeMesh3d hem = new HeMesh3d(); }
/// <summary> /// Applies a single iteration of Catmull-Clark subdivision to the given mesh. /// If using external buffers to store vertex attributes, the number of vertices after subdivision equals the sum of the number of vertices, edges, and faces in the initial mesh. /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="mesh"></param> /// <param name="position"></param> /// <param name="boundaryType"></param> public static void CatmullClark <V, E, F>(HeMesh <V, E, F> mesh, Property <V, Vec3d> position, SmoothBoundaryType boundaryType) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { // impl ref // http://rosettacode.org/wiki/Catmull%E2%80%93Clark_subdivision_surface // http://w3.impa.br/~lcruz/courses/cma/surfaces.html CatmullClarkGeometry(mesh, position, boundaryType); QuadSplitTopology(mesh); }
/// <summary> /// If using external buffers to store vertex attributes, the number of vertices in the resulting mesh equals 8 times the number of edges in the given mesh. /// </summary> /// <typeparam name="UV"></typeparam> /// <typeparam name="UE"></typeparam> /// <typeparam name="UF"></typeparam> /// <param name="mesh"></param> /// <param name="getPosition"></param> /// <param name="getScale"></param> /// <param name="getNormal"></param> /// <param name="getCenter"></param> /// <param name="setPosition"></param> /// <returns></returns> public TM CreateWeave <UV, UE, UF>(HeMesh <UV, UE, UF> mesh, Func <UV, Vec3d> getPosition, Func <UE, double> getScale, Func <UE, Vec3d> getNormal, Func <UF, Vec3d> getCenter, Action <TV, Vec3d> setPosition) where UV : HeMesh <UV, UE, UF> .Vertex where UE : HeMesh <UV, UE, UF> .Halfedge where UF : HeMesh <UV, UE, UF> .Face { int ne = mesh.Edges.Count; var result = Create(ne << 3, ne << 4, ne << 3); CreateWeaveGeometry(mesh, result, getPosition, getScale, getNormal, getCenter, setPosition); CreateWeaveTopology(mesh, result); return(result); }
/// <summary> /// Implementation currently ignores texture coordinates and normals. /// </summary> /// <typeparam name="TV"></typeparam> /// <typeparam name="TE"></typeparam> /// <typeparam name="TF"></typeparam> /// <param name="path"></param> /// <param name="mesh"></param> /// <param name="setPosition"></param> public static void ReadFromObj <TV, TE, TF>(string path, HeMesh <TV, TE, TF> mesh, Action <TV, Vec3d> setPosition) where TV : HeMesh <TV, TE, TF> .Vertex where TE : HeMesh <TV, TE, TF> .Halfedge where TF : HeMesh <TV, TE, TF> .Face { var verts = mesh.Vertices; var faces = mesh.Faces; var face = new List <int>(); using (var reader = new StreamReader(path)) { string line; while ((line = reader.ReadLine()) != null) { // skip empty lines and comments if (line.Length == 0 || line[0] == '#') { continue; } // check the first character var segments = line.Split(ObjUtil.Separators, StringSplitOptions.RemoveEmptyEntries); switch (segments[0]) { case "v": { // parse vertex double x = double.Parse(segments[1]); double y = double.Parse(segments[2]); double z = double.Parse(segments[3]); var v = mesh.AddVertex(); setPosition(v, new Vec3d(x, y, z)); break; } case "f": { // parse face for (int i = 1; i < segments.Length; i++) { var ids = segments[i].Split(ObjUtil.FaceSeparators); face.Add(int.Parse(ids[0]) - 1); } mesh.AddFace(face); face.Clear(); break; } } } } }
/* * /// <summary> * /// Applies a single iteration of Catmull-Clark subdivision to the given mesh. * /// http://rosettacode.org/wiki/Catmull%E2%80%93Clark_subdivision_surface * /// http://w3.impa.br/~lcruz/courses/cma/surfaces.html * /// </summary> * /// <typeparam name="V"></typeparam> * /// <typeparam name="E"></typeparam> * /// <typeparam name="F"></typeparam> * /// <param name="mesh"></param> * /// <param name="position"></param> * /// <param name="boundaryType"></param> * public static void CatmullClark<V, E, F>(HeMesh<V, E, F> mesh, Property<V, Vec3d> position, SmoothBoundaryType boundaryType, bool parallel) * where V : HeMeshBase<V, E, F>.Vertex * where E : HeMeshBase<V, E, F>.Halfedge * where F : HeMeshBase<V, E, F>.Face * { * CatmullClarkGeometry(mesh, position, boundaryType, parallel); * QuadSplitTopology(mesh); * } * * * /// <summary> * /// * /// </summary> * private static void CatmullClarkGeometry<V, E, F>(HeMesh<V, E, F> mesh, Property<V, Vec3d> position, SmoothBoundaryType boundaryType, bool parallel) * where V : HeMeshBase<V, E, F>.Vertex * where E : HeMeshBase<V, E, F>.Halfedge * where F : HeMeshBase<V, E, F>.Face * { * var verts = mesh.Vertices; * var edges = mesh.Edges; * var faces = mesh.Faces; * * int fv0 = verts.Count; // index of first face vertex * int ev0 = verts.Count + faces.Count; * * // add all new vertices * mesh.AddVertices(faces.Count); * mesh.AddVertices(edges.Count); * * // set attributes of face vertices * Action<Tuple<int, int>> setFaceVerts = range => * { * for (int i = range.Item1; i < range.Item2; i++) * { * var f = faces[i]; * * if (!f.IsUnused) * position.Set(verts[i + fv0], f.Vertices.Mean(position.Get)); * } * }; * * // set attributes of edge vertices * Action<Tuple<int, int>> setEdgeVerts = range => * { * for (int i = range.Item1; i < range.Item2; i++) * { * var he0 = edges[i]; * if (he0.IsUnused) continue; * * if (he0.IsBoundary) * { * position.Set(verts[i + ev0], he0.Lerp(position.Get, 0.5)); * continue; * } * * var he1 = he0.Twin; * var p0 = position.Get(he0.Start); * var p1 = position.Get(he1.Start); * var p2 = position.Get(verts[he0.Face.Index + fv0]); * var p3 = position.Get(verts[he1.Face.Index + fv0]); * position.Set(verts[i + ev0], (p0 + p1 + p2 + p3) * 0.25); * } * }; * * // set attributes of old vertices * //CatmullClarkSmooth(mesh, position, boundaryType); * Action<Tuple<int, int>> setOldVerts = range => * { * for (int i = range.Item1; i < range.Item2; i++) * { * var v = verts[i]; * if (v.IsUnused) continue; * * if (v.IsBoundary) * { * var he0 = v.FirstOut; * var he1 = he0.PrevInFace; * var p0 = position.Get(verts[(he0.Index >> 1) + ev0]); * var p1 = position.Get(verts[(he1.Index >> 1) + ev0]); * position.Set(v, position.Get(v) * 0.5 + (p0 + p1) * 0.25); * } * else * { * Vec3d fsum = new Vec3d(); * Vec3d esum = new Vec3d(); * int n = 0; * * foreach (var he in v.OutgoingHalfedges) * { * fsum += position.Get(verts[he.Face.Index + fv0]); * esum += position.Get(verts[(he.Index >> 1) + ev0]); * n++; * } * * double t = 1.0 / n; * position.Set(v, (position.Get(v) * (n - 3) + fsum * t + 2 * esum * t) * t); * } * } * }; * * * if (parallel) * { * Parallel.ForEach(Partitioner.Create(0, faces.Count), setFaceVerts); * Parallel.ForEach(Partitioner.Create(0, edges.Count), setEdgeVerts); * Parallel.ForEach(Partitioner.Create(0, verts.Count), setOldVerts); * } * else * { * setFaceVerts(Tuple.Create(0, faces.Count)); * setEdgeVerts(Tuple.Create(0, edges.Count)); * setOldVerts(Tuple.Create(0, verts.Count)); * } * } */ #endregion /// <summary> /// If using external buffers to store vertex attributes, the number of vertices after subdivision equals the sum of the number of vertices and faces in the initial mesh. /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="mesh"></param> /// <param name="position"></param> /// <param name="skipBoundary"></param> public static void Diagonalize <V, E, F>(HeMesh <V, E, F> mesh, Property <V, Vec3d> position, bool skipBoundary) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { var edges = mesh.Edges; var faces = mesh.Faces; int ne = edges.Count; int nf = faces.Count; // stellate faces for (int i = 0; i < nf; i++) { var f = faces[i]; if (f.IsUnused) { continue; } var v = mesh.AddVertex(); position.Set(v, f.GetBarycenter(position.Get)); mesh.PokeFaceImpl(f.First, v); } // merge faces if (skipBoundary) { for (int i = 0; i < ne; i++) { var he = edges[i]; if (he.IsUnused || he.IsBoundary) { continue; } mesh.MergeFaces(he); } } else { for (int i = 0; i < ne; i++) { var he = edges[i]; if (he.IsUnused) { continue; } mesh.MergeFaces(he); } } }
/// <summary> /// /// </summary> private static void CatmullClarkSmoothCornerFixed <V, E, F>(HeMesh <V, E, F> mesh, Property <V, Vec3d> position) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { var verts = mesh.Vertices; int ev0 = verts.Count - mesh.Edges.Count; // index of first edge vertex int fv0 = ev0 - mesh.Faces.Count; // index of first face vertex // set old vertices for (int i = 0; i < fv0; i++) { var v = verts[i]; if (v.IsUnused) { continue; } if (v.IsBoundary) { var he0 = v.First; if (he0.IsAtDegree2) { continue; // skip corner verts } var he1 = he0.Previous; var p0 = position.Get(verts[(he0.Index >> 1) + ev0]); var p1 = position.Get(verts[(he1.Index >> 1) + ev0]); position.Set(v, position.Get(v) * 0.5 + (p0 + p1) * 0.25); } else { Vec3d fsum = new Vec3d(); Vec3d esum = new Vec3d(); int n = 0; foreach (var he in v.OutgoingHalfedges) { fsum += position.Get(verts[he.Face.Index + fv0]); esum += position.Get(verts[(he.Index >> 1) + ev0]); n++; } double t = 1.0 / n; position.Set(v, (position.Get(v) * (n - 3) + fsum * t + 2 * esum * t) * t); } } }
/// <summary> /// /// </summary> private void CreateWeaveGeometry <UV, UE, UF>(HeMesh <UV, UE, UF> mesh, TM newMesh, Func <UV, Vec3d> getPosition, Func <UE, double> getScale, Func <UE, Vec3d> getNormal, Func <UF, Vec3d> getCenter, Action <TV, Vec3d> setPosition) where UV : HeMesh <UV, UE, UF> .Vertex where UE : HeMesh <UV, UE, UF> .Halfedge where UF : HeMesh <UV, UE, UF> .Face { var edges = mesh.Edges; int ne = edges.Count; // bulk add new vertices newMesh.AddVertices(ne << 3); var newVerts = newMesh.Vertices; // add vertices (8 per halfedge pair in m0) for (int i = 0; i < ne; i++) { var he0 = edges[i]; var he1 = he0.Twin; var f0 = he0.Face; var f1 = he1.Face; // scale points to mid point of edge Vec3d p0 = getPosition(he0.Start); Vec3d p1 = getPosition(he1.Start); Vec3d p = (p0 + p1) * 0.5; var t = getScale(he0); p0 = Vec3d.Lerp(p, p0, t); p1 = Vec3d.Lerp(p, p1, t); Vec3d p2 = (f0 == null) ? new Vec3d() : Vec3d.Lerp(p, getCenter(f0), t); Vec3d p3 = (f1 == null) ? new Vec3d() : Vec3d.Lerp(p, getCenter(f1), t); // set vertex positions Vec3d d = he0.IsBoundary ? Vec3d.Zero : getNormal(he0); int j = i << 3; setPosition(newVerts[j], p0 - d); setPosition(newVerts[j + 1], p2 - d); setPosition(newVerts[j + 2], p0 + d); setPosition(newVerts[j + 3], p2 + d); setPosition(newVerts[j + 4], p1 - d); setPosition(newVerts[j + 5], p3 - d); setPosition(newVerts[j + 6], p1 + d); setPosition(newVerts[j + 7], p3 + d); } }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="mesh"></param> /// <param name="position"></param> /// <param name="boundaryType"></param> private static void CatmullClarkGeometry <V, E, F>(HeMesh <V, E, F> mesh, Property <V, Vec3d> position, SmoothBoundaryType boundaryType) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { var verts = mesh.Vertices; int fv0 = verts.Count; // index of first face vertex // create face vertices foreach (var f in mesh.Faces) { var v = mesh.AddVertex(); if (!f.IsUnused) { position.Set(v, f.Vertices.Mean(position.Get)); } } // create edge vertices foreach (var he0 in mesh.Edges) { var v = mesh.AddVertex(); if (he0.IsUnused) { continue; } if (he0.IsBoundary) { var p = (position.Get(he0.Start) + position.Get(he0.End)) * 0.5; position.Set(v, p); continue; } var he1 = he0.Twin; var p0 = position.Get(he0.Start); var p1 = position.Get(he1.Start); var p2 = position.Get(verts[he0.Face.Index + fv0]); var p3 = position.Get(verts[he1.Face.Index + fv0]); position.Set(v, (p0 + p1 + p2 + p3) * 0.25); } // smooth old vertices CatmullClarkSmooth(mesh, position, boundaryType); }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="mesh"></param> private static void QuadSplitTopology <V, E, F>(HeMesh <V, E, F> mesh) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { var verts = mesh.Vertices; var hedges = mesh.Halfedges; var faces = mesh.Faces; int ne = hedges.Count >> 1; int nf = faces.Count; int ev0 = verts.Count - ne; // index of first edge vertex int fv0 = ev0 - nf; // index of first face vertex (also the number of vertices in the initial mesh) // split edges for (int i = 0; i < ne; i++) { var he = hedges[i << 1]; if (he.IsUnused) { continue; } var ev = verts[i + ev0]; mesh.SplitEdgeImpl(he, ev); } // split faces for (int i = 0; i < nf; i++) { var f = faces[i]; if (f.IsUnused) { continue; } var he = f.First; if (he.Start.Index >= fv0) { he = he.Previous; // ensure halfedge starts from an old vertex } mesh.QuadSplitFace(he, verts[i + fv0]); } }