/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="mesh"></param> /// <param name="quadrangulator"></param> /// <param name="getPosition"></param> /// <param name="getNormal"></param> /// <param name="getTexture"></param> /// <param name="getColor"></param> /// <returns></returns> public static Mesh ToMesh <V, E, F>(this HeMesh <V, E, F> mesh, Func <V, Point3f> getPosition, Func <V, Vector3f> getNormal = null, Func <V, Point2f> getTexture = null, Func <V, Color> getColor = null, IFaceQuadrangulator <V, E, F> quadrangulator = null) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { return(RhinoFactory.Mesh.CreateFromHeMesh(mesh, getPosition, getNormal, getTexture, getColor, quadrangulator)); }
/// <summary> /// /// </summary> /// <typeparam name="TV"></typeparam> /// <typeparam name="TE"></typeparam> /// <typeparam name="TF"></typeparam> /// <param name="mesh"></param> /// <param name="path"></param> /// <param name="getTexture"></param> public static void WriteToObj <TV, TE, TF>(HeMesh <TV, TE, TF> mesh, string path, Func <TV, Vector2d> 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, Position3d <TV> .Get, Normal3d <TV> .Get, getTexture); }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="mesh"></param> /// <param name="quadrangulator"></param> /// <returns></returns> public static Mesh ToMesh <V, E, F>(this HeMesh <V, E, F> mesh, IFaceQuadrangulator <V, E, F> quadrangulator = null) where V : HeMesh <V, E, F> .Vertex, IPosition3d where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { return(RhinoFactory.Mesh.CreateFromHeMesh(mesh, v => (Point3f)v.Position, null, null, null, quadrangulator)); }
/// <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> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="mesh"></param> /// <param name="getColor"></param> /// <param name="quadrangulator"></param> /// <returns></returns> public static Mesh ToPolySoup <V, E, F>(this HeMesh <V, E, F> mesh, Func <F, Color> getColor = null, IFaceQuadrangulator quadrangulator = null) where V : HeMesh <V, E, F> .Vertex, IPosition3d where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { return(RhinoFactory.Mesh.CreatePolySoup(mesh, v => v.Position.As3f, getColor, quadrangulator)); }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <param name="mesh"></param> /// <param name="getPosition"></param> /// <param name="getColor"></param> /// <param name="quadrangulator"></param> /// <returns></returns> public static Mesh ToPolySoup <V, E, F>(this HeMesh <V, E, F> mesh, Func <V, Point3f> getPosition, Func <F, Color> getColor = null, IFaceQuadrangulator <V, E, F> quadrangulator = null) where V : HeMesh <V, E, F> .Vertex where E : HeMesh <V, E, F> .Halfedge where F : HeMesh <V, E, F> .Face { return(RhinoFactory.Mesh.CreatePolySoup(mesh, getPosition, getColor, quadrangulator)); }
/// <summary> /// /// </summary> /// <typeparam name="V"></typeparam> /// <typeparam name="E"></typeparam> /// <typeparam name="F"></typeparam> /// <typeparam name="VA"></typeparam> /// <typeparam name="EA"></typeparam> /// <typeparam name="FA"></typeparam> /// <param name="path"></param> /// <param name="mesh"></param> /// <param name="setVertexAttributes"></param> /// <param name="setHedgeAttributes"></param> /// <param name="setFaceAttributes"></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 = Interop.DeserializeJson <HeMeshJsonBuffer <VA, EA, FA> >(path); buffer.ReadTo(mesh, setVertexAttributes, setHedgeAttributes, setFaceAttributes); }
/// <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> /// /// </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> /// <typeparam name="F"></typeparam> /// <typeparam name="VA"></typeparam> /// <typeparam name="EA"></typeparam> /// <typeparam name="FA"></typeparam> /// <param name="mesh"></param> /// <param name="path"></param> /// <param name="getVertexAttributes"></param> /// <param name="getHedgeAttributes"></param> /// <param name="getFaceAttributes"></param> 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); Interop.SerializeJson(buffer, path); }
/// <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, Vector3d> getPosition, Func <UE, double> getScale, Func <UE, Vector3d> getNormal, Func <UF, Vector3d> getCenter, Action <TV, Vector3d> 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, Vector3d> 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 Vector3d(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> /// /// </summary> private void CreateWeaveGeometry <UV, UE, UF>(HeMesh <UV, UE, UF> mesh, TM newMesh, Func <UV, Vector3d> getPosition, Func <UE, double> getScale, Func <UE, Vector3d> getNormal, Func <UF, Vector3d> getCenter, Action <TV, Vector3d> 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 Vector3d p0 = getPosition(he0.Start); Vector3d p1 = getPosition(he1.Start); Vector3d p = (p0 + p1) * 0.5; var t = getScale(he0); p0 = Vector3d.Lerp(p, p0, t); p1 = Vector3d.Lerp(p, p1, t); Vector3d p2 = (f0 == null) ? new Vector3d() : Vector3d.Lerp(p, getCenter(f0), t); Vector3d p3 = (f1 == null) ? new Vector3d() : Vector3d.Lerp(p, getCenter(f1), t); // set vertex positions Vector3d d = he0.IsBoundary ? Vector3d.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="UV"></typeparam> /// <typeparam name="UE"></typeparam> /// <typeparam name="UF"></typeparam> /// <param name="mesh"></param> /// <param name="componentIndices"></param> /// <param name="edgeIndices"></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, out int[] componentIndices, out int[] edgeIndices, 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 { int ne = mesh.Edges.Count; componentIndices = new int[ne]; edgeIndices = new int[ne]; return(CreateConnectedComponents( mesh, HeMesh <UV, UE, UF> .Edge.CreateProperty(componentIndices), HeMesh <UV, UE, UF> .Edge.CreateProperty(edgeIndices), setVertex, setHedge, setFace )); }
/// <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> public void AppendFaceTopology <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 { int nhe = Halfedges.Count; int nv = Vertices.Count; var meshHedges = mesh.Halfedges; var meshFaces = mesh.Faces; // append new elements for (int i = 0; i < meshFaces.Count; i++) { AddVertex(); } for (int i = 0; i < meshHedges.Count; i += 2) { AddEdge(); } // set vertex refs for (int i = 0; i < meshFaces.Count; i++) { var fB = meshFaces[i]; var vA = Vertices[i + nv]; // transfer attributes setVertex?.Invoke(vA, fB); if (fB.IsUnused) { continue; } var heB = fB.First; // find first interior halfedge in the face while (heB.Twin.Face == null) { heB = heB.Next; if (heB == fB.First) { goto EndFor; // dual vertex has no valid halfedges } } vA.First = Halfedges[heB + nhe]; EndFor :; } // set halfedge refs for (int i = 0; i < meshHedges.Count; i++) { var heB0 = meshHedges[i]; var heA0 = Halfedges[i + nhe]; // transfer attributes setHedge?.Invoke(heA0, heB0); if (heB0.IsUnused || heB0.IsBoundary) { continue; } var heB1 = heB0; // find next interior halfedge in the face do { heB1 = heB1.Next; }while (heB1.Twin.Face == null && heB1 != heB0); heA0.Start = Vertices[heB0.Face + nv]; heA0.Twin.MakeConsecutive(Halfedges[heB1 + nhe]); //heA0.MakeConsecutive(Halfedges[heB1 + nhe]); // TODO check this } }
/// <summary> /// Reads this buffer to the given mesh. /// </summary> public void ReadTo <V, E, F>(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 verts = mesh.Vertices; int nv = verts.Count; var hedges = mesh.Halfedges; int nhe = hedges.Count; var faces = mesh.Faces; int nf = faces.Count; // add new vertices for (int i = 0; i < _vertexRefs.Length; i++) { mesh.AddVertex(); } // add new halfedges for (int i = 0; i < _hedgeRefs.Length; i += 2) { mesh.AddEdge(); } // add new faces for (int i = 0; i < _faceRefs.Length; i++) { mesh.AddFace(); } // link up vertices for (int i = 0; i < _vertexRefs.Length; i++) { var v = verts[i + nv]; var first = _vertexRefs[i]; if (first != -1) { v.First = hedges[first + nhe]; } } // link up halfedges for (int i = 0; i < _hedgeRefs.Length; i++) { var he = hedges[i + nhe]; var refs = _hedgeRefs[i]; var prev = refs[0]; if (prev != -1) { he.Previous = hedges[prev + nhe]; } he.Next = hedges[refs[1] + nhe]; he.Start = verts[refs[2] + nv]; var face = refs[3]; if (face != -1) { he.Face = faces[face + nf]; } } // link up faces for (int i = 0; i < _faceRefs.Length; i++) { var f = faces[i + nf]; var first = _faceRefs[i]; if (first != -1) { f.First = hedges[first + nhe]; } } // TODO validate topology? // set vertex attributes if (setVertexAttributes != null) { for (int i = 0; i < _vertexAttributes.Length; i++) { setVertexAttributes(verts[i + nv], _vertexAttributes[i]); } } // set vertex attributes if (setHedgeAttributes != null) { for (int i = 0; i < _hedgeAttributes.Length; i++) { setHedgeAttributes(hedges[i + nhe], _hedgeAttributes[i]); } } // set vertex attributes if (setFaceAttributes != null) { for (int i = 0; i < _faceAttributes.Length; i++) { setFaceAttributes(faces[i + nf], _faceAttributes[i]); } } }
/// <summary> /// Action delegates specify how attributes of parent elements are mapped to attributes of component elements. /// </summary> /// <typeparam name="UV"></typeparam> /// <typeparam name="UE"></typeparam> /// <typeparam name="UF"></typeparam> /// <param name="mesh"></param> /// <param name="componentIndex"></param> /// <param name="edgeIndex"></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, Property <UE, int> componentIndex, Property <UE, int> edgeIndex, 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 vertices = mesh.Vertices; var hedges = mesh.Halfedges; var faces = mesh.Faces; int ncomp = mesh.GetEdgeComponentIndices(componentIndex.Set); var comps = new TM[ncomp]; // initialize components for (int i = 0; i < comps.Length; i++) { comps[i] = Create(); } // create component halfedges for (int i = 0; i < hedges.Count; i += 2) { var heA = hedges[i]; if (heA.IsUnused) { continue; } var comp = comps[componentIndex.Get(heA)]; var heB = comp.AddEdge(); edgeIndex.Set(heA, heB.Index >> 1); } // set component halfedge->halfedge refs for (int i = 0; i < hedges.Count; i++) { var heA0 = hedges[i]; if (heA0.IsUnused) { continue; } // the component to which heA0 was copied var compHedges = comps[componentIndex.Get(heA0)].Halfedges; var heA1 = heA0.Next; // set refs var heB0 = compHedges[(edgeIndex.Get(heA0) << 1) + (i & 1)]; var heB1 = compHedges[(edgeIndex.Get(heA1) << 1) + (heA1.Index & 1)]; heB0.MakeConsecutive(heB1); // transfer attributes setHedge?.Invoke(heB0, heA0); } // create component vertices for (int i = 0; i < vertices.Count; i++) { var vA = vertices[i]; if (vA.IsUnused) { continue; } var heA = vA.First; var comp = comps[componentIndex.Get(heA)]; var heB = comp.Halfedges[(edgeIndex.Get(heA) << 1) + (heA.Index & 1)]; // set vertex refs var vB = comp.AddVertex(); vB.First = heB; foreach (var he in heB.CirculateStart) { he.Start = vB; } // transfer attributes setVertex?.Invoke(vB, vA); } // create component faces for (int i = 0; i < faces.Count; i++) { var fA = faces[i]; if (fA.IsUnused) { continue; } var heA = fA.First; var comp = comps[componentIndex.Get(heA)]; var heB = comp.Halfedges[(edgeIndex.Get(heA) << 1) + (heA.Index & 1)]; // set face refs var fB = comp.AddFace(); fB.First = heB; foreach (var he in heB.Circulate) { he.Face = fB; } // transfer attributes setFace?.Invoke(fB, fA); } return(comps); }
/// <summary> /// /// </summary> /// <typeparam name="TV"></typeparam> /// <typeparam name="TE"></typeparam> /// <typeparam name="TF"></typeparam> /// <param name="mesh"></param> /// <param name="path"></param> /// <param name="getPosition"></param> /// <param name="getNormal"></param> /// <param name="getTexture"></param> public static void WriteToObj <TV, TE, TF>(HeMesh <TV, TE, TF> mesh, string path, Func <TV, Vector3d> getPosition, Func <TV, Vector3d> getNormal = null, Func <TV, Vector2d> getTexture = null) 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; int formatIndex = 0; using (var writer = new StreamWriter(path, false)) { writer.WriteLine(ObjUtil.Header); writer.WriteLine(); // write vertex positions for (int i = 0; i < verts.Count; i++) { var v = getPosition(verts[i]); writer.WriteLine("v {0} {1} {2}", v.X, v.Y, v.Z); } // write vertex normals if (getNormal != null) { for (int i = 0; i < verts.Count; i++) { var vn = getNormal(verts[i]); writer.WriteLine("vn {0} {1} {2}", vn.X, vn.Y, vn.Z); } formatIndex |= 2; } // write vertex texCoords if (getTexture != null) { for (int i = 0; i < verts.Count; i++) { var vt = getTexture(verts[i]); writer.WriteLine("vt {0} {1}", vt.X, vt.Y); } formatIndex |= 1; } // write faces string format = ObjUtil.FaceFormats[formatIndex]; for (int i = 0; i < faces.Count; i++) { var f = faces[i]; if (f.IsUnused) { continue; } writer.Write("f"); foreach (var v in f.Vertices) { writer.Write(format, v.Index + 1); } writer.WriteLine(); } } }