public bool Apply() { EdgeLoop useLoop = null; if (FillLoop == null) { MeshBoundaryLoops loops = new MeshBoundaryLoops(Mesh, true); if (loops.Count == 0) { return(false); } if (BorderHintTris != null) { useLoop = select_loop_tris_hint(loops); } if (useLoop == null && loops.MaxVerticesLoopIndex >= 0) { useLoop = loops[loops.MaxVerticesLoopIndex]; } } else { useLoop = FillLoop; } if (useLoop == null) { return(false); } // step 1: do stupid hole fill SimpleHoleFiller filler = new SimpleHoleFiller(Mesh, useLoop); if (filler.Fill() == false) { return(false); } if (useLoop.Vertices.Length <= 3) { FillTriangles = filler.NewTriangles; FillVertices = new int[0]; return(true); } MeshFaceSelection tris = new MeshFaceSelection(Mesh); tris.Select(filler.NewTriangles); // extrude initial fill surface (this is used in socketgen for example) if (OffsetDistance > 0) { MeshExtrudeFaces extrude = new MeshExtrudeFaces(Mesh, tris); extrude.ExtrudedPositionF = (v, n, vid) => { return(v + OffsetDistance * OffsetDirection); }; if (!extrude.Extrude()) { return(false); } tris.Select(extrude.JoinTriangles); } // if we aren't trying to stay inside hole, expand out a bit, // which allows us to clean up ugly edges if (ConstrainToHoleInterior == false) { tris.ExpandToOneRingNeighbours(2); tris.LocalOptimize(true, true); } // remesh the initial coarse fill region if (RemeshBeforeSmooth) { RegionRemesher remesh = new RegionRemesher(Mesh, tris); remesh.SetTargetEdgeLength(TargetEdgeLength); remesh.EnableSmoothing = (SmoothAlpha > 0); remesh.SmoothSpeedT = SmoothAlpha; if (ConfigureRemesherF != null) { ConfigureRemesherF(remesh, true); } for (int k = 0; k < InitialRemeshPasses; ++k) { remesh.BasicRemeshPass(); } remesh.BackPropropagate(); tris = new MeshFaceSelection(Mesh); tris.Select(remesh.CurrentBaseTriangles); if (ConstrainToHoleInterior == false) { tris.LocalOptimize(true, true); } } if (ConstrainToHoleInterior) { for (int k = 0; k < SmoothSolveIterations; ++k) { smooth_and_remesh_preserve(tris, k == SmoothSolveIterations - 1); tris = new MeshFaceSelection(Mesh); tris.Select(FillTriangles); } } else { smooth_and_remesh(tris); tris = new MeshFaceSelection(Mesh); tris.Select(FillTriangles); } MeshVertexSelection fill_verts = new MeshVertexSelection(Mesh); fill_verts.SelectInteriorVertices(tris); FillVertices = fill_verts.ToArray(); return(true); }
public static void test_uv_insert_string() { DMesh3 mesh = TestUtil.LoadTestInputMesh("plane_xy_25x25.obj"); mesh.EnableVertexUVs(Vector2f.Zero); DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh); spatial.Build(); int tid = spatial.FindNearestTriangle(Vector3d.Zero); PolygonFont2d font = PolygonFont2d.ReadFont("c:\\scratch\\font.bin"); //List<GeneralPolygon2d> letter = new List<GeneralPolygon2d>(font.Characters.First().Value.Polygons); //double targetWidth = 20.0f; List <GeneralPolygon2d> letter = font.GetCharacter('a'); double targetWidth = 10.0f; AxisAlignedBox2d bounds = font.MaxBounds; Vector2d center = bounds.Center; Vector2d scale2d = (targetWidth / font.MaxBounds.Width) * new Vector2d(1, 1); for (int li = 0; li < letter.Count; ++li) { GeneralPolygon2d gp = new GeneralPolygon2d(letter[li]); gp.Scale(scale2d, center); gp.Translate(-center); letter[li] = gp; } List <MeshFaceSelection> letter_interiors = new List <MeshFaceSelection>(); bool bSimplify = true; for (int li = 0; li < letter.Count; ++li) { GeneralPolygon2d gp = letter[li]; MeshInsertUVPolyCurve outer = new MeshInsertUVPolyCurve(mesh, gp.Outer); Util.gDevAssert(outer.Validate() == ValidationStatus.Ok); outer.Apply(); if (bSimplify) { outer.Simplify(); } List <MeshInsertUVPolyCurve> holes = new List <MeshInsertUVPolyCurve>(gp.Holes.Count); for (int hi = 0; hi < gp.Holes.Count; ++hi) { MeshInsertUVPolyCurve insert = new MeshInsertUVPolyCurve(mesh, gp.Holes[hi]); Util.gDevAssert(insert.Validate() == ValidationStatus.Ok); insert.Apply(); if (bSimplify) { insert.Simplify(); } holes.Add(insert); } // find a triangle connected to loop that is inside the polygon // [TODO] maybe we could be a bit more robust about this? at least // check if triangle is too degenerate... int seed_tri = -1; EdgeLoop outer_loop = outer.Loops[0]; for (int i = 0; i < outer_loop.EdgeCount; ++i) { if (!mesh.IsEdge(outer_loop.Edges[i])) { continue; } Index2i et = mesh.GetEdgeT(outer_loop.Edges[i]); Vector3d ca = mesh.GetTriCentroid(et.a); bool in_a = gp.Outer.Contains(ca.xy); Vector3d cb = mesh.GetTriCentroid(et.b); bool in_b = gp.Outer.Contains(cb.xy); if (in_a && in_b == false) { seed_tri = et.a; break; } else if (in_b && in_a == false) { seed_tri = et.b; break; } } Util.gDevAssert(seed_tri != -1); // make list of all outer & hole edges HashSet <int> loopEdges = new HashSet <int>(outer_loop.Edges); foreach (var insertion in holes) { foreach (int eid in insertion.Loops[0].Edges) { loopEdges.Add(eid); } } // flood-fill inside loop from seed triangle MeshFaceSelection sel = new MeshFaceSelection(mesh); sel.FloodFill(seed_tri, null, (eid) => { return(loopEdges.Contains(eid) == false); }); letter_interiors.Add(sel); } // extrude regions Func <Vector3d, Vector3f, int, Vector3d> OffsetF = (v, n, i) => { return(v + Vector3d.AxisZ); }; foreach (var interior in letter_interiors) { MeshExtrudeFaces extrude = new MeshExtrudeFaces(mesh, interior); extrude.ExtrudedPositionF = OffsetF; extrude.Extrude(); } TestUtil.WriteTestOutputMesh(mesh, "insert_uv_string.obj"); }