public async Task <ActionResult> SearchRefList(string q, int pageIndex = 1) { if (string.IsNullOrWhiteSpace(q)) { var vm = new SearchPageViewModel() { TextSearch = q, PageIndex = pageIndex, PageCount = 1, RefLists = EmptyRefListViewModels, }; return(JsonNet(vm, JsonRequestBehavior.AllowGet)); } else { var pageSize = 20; var start = (pageIndex - 1) * pageSize; var result = await _refListHandler.SearchRefListsAsync(q, start, 20); var vm = new SearchPageViewModel() { TextSearch = q, PageIndex = pageIndex, PageCount = IndexUtil.GetPageCount(result.WholeCount, pageSize), RefLists = Mapper.Map <ICollection <RefListViewModel> >(result.RefLists), }; return(JsonNet(vm, JsonRequestBehavior.AllowGet)); } }
/// <summary> /// createdUserIdが作成したRefListを返します。 /// </summary> public async Task <PagedRefLists> GetRefListsAsync(GetRefListsRequest req) { var query = _refsContext.RefLists.Include("Author").Include("TagUses.Tag").Include("Statistics").AsNoTracking(); query = AppendQueryForAuthorId(query, req.AuthorId); query = AppendQueryForAuthorUserName(query, req.AuthorUserName); query = AppendQueryForTagUseId(query, req.TagUseId); query = AppendQueryForTagName(query, req.TagName, req.TagUseId); query = AppendQueryForFromDate(query, req.FromDate); query = AppendQueryForSearchText(query, req.TitleSearch); query = AppendQueryForPublishingStatus(query, req.PublishingStatusCondition); var storedRefListCount = await query.CountAsync(); query = QueryUtil.AppendQueryForRefListSort(query, req.Sort); query = query.Skip(req.PageIndex * req.PageSize); query = query.Take(req.PageSize); var storedLists = await query.ToArrayAsync(); var ret = new PagedRefLists() { PageIndex = req.PageIndex, PageCount = IndexUtil.GetPageCount(storedRefListCount, req.PageSize), AllRefListCount = storedRefListCount, RefLists = storedLists, }; return(ret); }
/// <summary> /// 发送消息的点击事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Button_Click(object sender, RoutedEventArgs e) { RichTextBoxUtil richTextBoxUtil = new RichTextBoxUtil(this.ChatingContentMsg); IndexUtil.SendData(richTextBoxUtil); SendData(richTextBoxUtil); }
public async Task <ActionResult> Search(string q = "") { var vm = new SearchPageViewModel() { TextSearch = q, PageIndex = 1, }; if (!string.IsNullOrWhiteSpace(q)) { if (q.StartsWith("http://") || q.StartsWith("https://")) { var url = Uri.EscapeUriString(q); return(RedirectToAction("FindByBookmarklet", new { url = url, title = q, })); } else { var result = await _refListHandler.SearchRefListsAsync(q, 0, 20); vm.PageCount = IndexUtil.GetPageCount(result.WholeCount, 20); vm.RefLists = Mapper.Map <ICollection <RefListViewModel> >(result.RefLists); } } else { vm.PageCount = 1; vm.RefLists = EmptyRefListViewModels; } return(View(vm)); }
public static void InitDatabase() { if (DatabasePath != DatabaseConstants.IN_MEMORY_DB_PATH) { CommonFileUtil.EnsureFileExists(DatabasePath); } using (var db = CreateSqlConnection()) { db.Open(); // Enable write-ahead logging var walCommand = db.CreateCommand(); walCommand.CommandText = @"PRAGMA journal_mode = 'wal'"; walCommand.ExecuteNonQuery(); using (var txn = db.BeginTransaction()) { DatabaseTablesUtil.InitTables(db); TriggersUtil.InitTriggers(db); IndexUtil.InitIndices(db); InitGlobalSettings(db); txn.Commit(); } } }
void find_crease_edges(double angle_tol) { CreaseEdges = new HashSet <int>(); BoundaryEdges = new HashSet <int>(); double dot_tol = Math.Cos(angle_tol * MathUtil.Deg2Rad); foreach (int eid in Mesh.EdgeIndices()) { Index2i et = Mesh.GetEdgeT(eid); if (et.b == DMesh3.InvalidID) { BoundaryEdges.Add(eid); continue; } Vector3d n0 = Mesh.GetTriNormal(et.a); Vector3d n1 = Mesh.GetTriNormal(et.b); if (Math.Abs(n0.Dot(n1)) < dot_tol) { CreaseEdges.Add(eid); } } AllEdges = new HashSet <int>(CreaseEdges);; foreach (int eid in BoundaryEdges) { AllEdges.Add(eid); } AllVertices = new HashSet <int>(); IndexUtil.EdgesToVertices(Mesh, AllEdges, AllVertices); }
public bool Compute() { PlanarMesh = BuildPlanarMesh(false); InflatedMesh = ComputeInflation(PlanarMesh); DMesh3 remeshed = GenerateRemesh(InflatedMesh); Flatten(remeshed); ResultMesh = ComputeInflation(remeshed); MeshBoundaryLoops loops = new MeshBoundaryLoops(ResultMesh); DMesh3 otherSide = new DMesh3(ResultMesh); foreach (int vid in otherSide.VertexIndices()) { Vector3d v = otherSide.GetVertex(vid); v.z = -v.z; otherSide.SetVertex(vid, v); } otherSide.ReverseOrientation(); MeshEditor editor = new MeshEditor(ResultMesh); int[] mapVArray; editor.AppendMesh(otherSide, out mapVArray); IndexMap mapV = new IndexMap(mapVArray); foreach (EdgeLoop loop in loops) { int[] otherLoop = (int[])loop.Vertices.Clone(); IndexUtil.Apply(otherLoop, mapV); editor.StitchLoop(loop.Vertices, otherLoop); } Remesher remesh = new Remesher(ResultMesh); remesh.SetTargetEdgeLength(TargetEdgeLength); remesh.SmoothSpeedT = 0.25f; for (int k = 0; k < 10; ++k) { remesh.BasicRemeshPass(); } ResultMesh = new DMesh3(ResultMesh, true); LaplacianMeshSmoother smoother = new LaplacianMeshSmoother(ResultMesh); foreach (int vid in ResultMesh.VertexIndices()) { smoother.SetConstraint(vid, ResultMesh.GetVertex(vid), 0.5f, false); } smoother.SolveAndUpdateMesh(); return(true); }
// return same indices as GetEdgeV, but oriented based on attached triangle Index2i get_oriented_edgev(int eID, int tid_in, int tid_out) { Index2i edgev = Mesh.GetEdgeV(eID); int a = edgev.a, b = edgev.b; Index3i tri = Mesh.GetTriangle(tid_in); int ai = IndexUtil.find_edge_index_in_tri(a, b, ref tri); return(new Index2i(tri[ai], tri[(ai + 1) % 3])); }
/// <summary> /// 发送消息的回车事件即按下enter键就可以发送消息 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ChatingContentMsg_KeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Return) { RichTextBoxUtil richTextBoxUtil = new RichTextBoxUtil(this.ChatingContentMsg); IndexUtil.SendData(richTextBoxUtil); SendData(richTextBoxUtil); } }
/// <summary> /// Remove vertex vID, and all connected triangles if bRemoveAllTriangles = true /// (if false, them throws exception if there are still any triangles!) /// if bPreserveManifold, checks that we will not create a bowtie vertex first /// </summary> public MeshResult RemoveVertex(int vID, bool bRemoveAllTriangles = true, bool bPreserveManifold = true) { if (vertices_refcount.IsValid(vID) == false) { return(MeshResult.Failed_NotAVertex); } if (bRemoveAllTriangles) { // if any one-ring vtx is a boundary vtx and one of its outer-ring edges is an // interior edge then we will create a bowtie if we remove that triangle if (bPreserveManifold) { foreach (int tid in VtxTrianglesItr(vID)) { Index3i tri = GetTriangle(tid); int j = IndexUtil.find_tri_index(vID, tri); int oa = tri[(j + 1) % 3], ob = tri[(j + 2) % 3]; int eid = Find_edge(oa, ob); if (Edge_is_boundary(eid)) { continue; } if (Vertex_is_boundary(oa) || Vertex_is_boundary(ob)) { return(MeshResult.Failed_WouldCreateBowtie); } } } List <int> tris = new List <int>(); GetVtxTriangles(vID, tris, true); foreach (int tID in tris) { MeshResult result = RemoveTriangle(tID, false, bPreserveManifold); if (result != MeshResult.Ok) { return(result); } } } if (vertices_refcount.RefCount(vID) != 1) { throw new NotImplementedException("DMesh3.RemoveVertex: vertex is still referenced"); } vertices_refcount.Decrement(vID); Debug.Assert(vertices_refcount.IsValid(vID) == false); vertex_edges[vID] = null; UpdateTimeStamp(true); return(MeshResult.Ok); }
public Task <IEnumerable <ICategoryItem> > QueryCategories() { FileInfo lemmataTxt = DataFolder.GetFileIgnoreCase(LemmataTxt); if (lemmataTxt.Exists) { return(Task.FromResult(IndexUtil.LoadCategories(lemmataTxt, Ini).AsEnumerable <ICategoryItem>())); } return(Task.FromResult(Enumerable.Empty <ICategoryItem>())); }
public Task <IEnumerable <IIndexItem> > QueryIndex(string pattern, IEnumerable <ICategoryItem> categories, int page) { FileInfo lemmataTxt = DataFolder.GetFileIgnoreCase(LemmataTxt); if (lemmataTxt.Exists) { return(Task.FromResult(IndexUtil.LoadLemmata(lemmataTxt, pattern, categories, page) .AsEnumerable <IIndexItem>())); } return(Task.FromResult(Enumerable.Empty <IIndexItem>())); }
double compute_gauss_curvature(int vid) { double angle_sum = 0; exterior_angle_sums.TryGetValue(vid, out angle_sum); foreach (int tid in fillmesh.VtxTrianglesItr(vid)) { Index3i et = fillmesh.GetTriangle(tid); int idx = IndexUtil.find_tri_index(vid, ref et); angle_sum += fillmesh.GetTriInternalAngleR(tid, idx); } return(angle_sum - MathUtil.TwoPI); }
public void BridgeEdges(int ea, int eb, bool bInteractive) { if (PreviewMesh.IsEdge(ea) == false || PreviewMesh.IsEdge(eb) == false) { DebugUtil.Log("MeshEditorTool.BridgeEdges: invalid eid!"); return; } if (PreviewMesh.IsBoundaryEdge(ea) == false || PreviewMesh.IsBoundaryEdge(eb) == false) { DebugUtil.Log("MeshEditorTool.BridgeEdges: edge is not boundary edge!"); return; } Index2i eva = PreviewMesh.GetOrientedBoundaryEdgeV(ea); Index2i evb = PreviewMesh.GetOrientedBoundaryEdgeV(eb); AddTrianglesMeshChange addChange = null; previewSO.EditAndUpdateMesh((mesh) => { int shared_v = IndexUtil.find_shared_edge_v(ref eva, ref evb); if (shared_v == DMesh3.InvalidID) { int t1 = mesh.AppendTriangle(eva.b, eva.a, evb.b); int t2 = mesh.AppendTriangle(evb.b, evb.a, eva.b); PreviewSpatial.AddTriangle(t1); PreviewSpatial.AddTriangle(t2); addChange = new AddTrianglesMeshChange(); addChange.InitializeFromExisting(mesh, null, new List <int>() { t1, t2 }); } else { int ovb = (evb.a == shared_v) ? evb.b : evb.a; int t = mesh.AppendTriangle(eva.b, eva.a, ovb); PreviewSpatial.AddTriangle(t); addChange = new AddTrianglesMeshChange(); addChange.InitializeFromExisting(mesh, null, new List <int>() { t }); } }, GeometryEditTypes.ArbitraryEdit); if (addChange != null) { add_change(addChange, bInteractive); } }
// TODO: // - (in merge coincident) don't merge tris with same/opposite normals (option) // - after orienting components, try to find adjacent open components and // transfer orientation between them // - orient via nesting public void OrientComponents() { Components = new List <Component>(); HashSet <int> remaining = new HashSet <int>(Mesh.TriangleIndices()); List <int> stack = new List <int>(); while (remaining.Count > 0) { Component c = new Component(); c.triangles = new List <int>(); stack.Clear(); int start = remaining.First(); remaining.Remove(start); c.triangles.Add(start); stack.Add(start); while (stack.Count > 0) { int cur = stack[stack.Count - 1]; stack.RemoveAt(stack.Count - 1); Index3i tcur = Mesh.GetTriangle(cur); Index3i nbrs = Mesh.GetTriNeighbourTris(cur); for (int j = 0; j < 3; ++j) { int nbr = nbrs[j]; if (remaining.Contains(nbr) == false) { continue; } int a = tcur[j]; int b = tcur[(j + 1) % 3]; Index3i tnbr = Mesh.GetTriangle(nbr); if (IndexUtil.find_tri_ordered_edge(b, a, ref tnbr) == DMesh3.InvalidID) { Mesh.ReverseTriOrientation(nbr); } stack.Add(nbr); remaining.Remove(nbr); c.triangles.Add(nbr); } } Components.Add(c); } }
public async Task <PagedRefLists> GetAllFavoriteRefListsAsync(long ownerId, PageCondition pageCondition, RefListSortKind sort) { var refListIdsQuery = _refsContext.Favorites. Where(f => f.OwnerId == ownerId && f.Kind == FavoriteKind.RefList). Select(f => f.RefListId.Value); var refListIds = await refListIdsQuery.ToArrayAsync(); var tagIdsQuery = _refsContext.Favorites. Where(f => f.OwnerId == ownerId && f.Kind == FavoriteKind.Tag). Select(f => f.TagId.Value); var tagIds = await tagIdsQuery.ToArrayAsync(); var userIdsQuery = _refsContext.Favorites. Where(f => f.OwnerId == ownerId && f.Kind == FavoriteKind.User). Select(f => f.UserId.Value); var userIds = await userIdsQuery.ToArrayAsync(); var query = _refsContext.RefLists.AsNoTracking(). Where(l => l.PublishingStatus == PublishingStatusKind.Publish && ( refListIds.Contains(l.Id) || tagIds.Intersect(l.TagUses.Select(u => u.TagId)).Any() || userIds.Contains(l.AuthorId) ) ); var refListCount = await query.CountAsync(); query = QueryUtil.AppendQueryForRefListSort(query, sort); query = query.Skip(pageCondition.PageIndex * pageCondition.PageSize); query = query.Take(pageCondition.PageSize); var storedRefList = await query.ToArrayAsync(); return(new PagedRefLists() { PageIndex = pageCondition.PageIndex, PageCount = IndexUtil.GetPageCount(refListCount, pageCondition.PageSize), RefLists = storedRefList, AllRefListCount = refListCount, }); }
protected virtual bool BackPropagate(RegionOperator regionOp, int[] insertedPolyVerts, EdgeLoop insertedLoop) { bool bOK = regionOp.BackPropropagate(); if (bOK) { ModifiedRegion = regionOp; IndexUtil.Apply(insertedPolyVerts, regionOp.ReinsertSubToBaseMapV); InsertedPolygonVerts = insertedPolyVerts; if (insertedLoop != null) { InsertedLoop = MeshIndexUtil.MapLoopViaVertexMap(regionOp.ReinsertSubToBaseMapV, regionOp.Region.SubMesh, regionOp.Region.BaseMesh, insertedLoop); if (RemovePolygonInterior) { InsertedLoop.CorrectOrientation(); } } } return(bOK); }
public async Task <PagedRefLists> GetFavoriteRefListsAsync( long ownerId, PageCondition pageCondition, FavoriteRefListSortKind sort ) { var query = _refsContext.Favorites. Include("RefList").Include("RefList.Author").Include("RefList.TagUses.Tag").Include("RefList.Statistics").AsNoTracking(). Where(f => f.OwnerId == ownerId && f.Kind == FavoriteKind.RefList); var count = await query.CountAsync(); query = QueryUtil.AppendQueryForFavoriteRefListSort(query, sort); query = query.Skip(pageCondition.PageIndex * pageCondition.PageSize); query = query.Take(pageCondition.PageSize); var storedFavs = await query.ToArrayAsync(); return(new PagedRefLists() { PageIndex = pageCondition.PageIndex, PageCount = IndexUtil.GetPageCount(count, pageCondition.PageSize), RefLists = storedFavs.Select(f => f.RefList), AllRefListCount = count, }); }
// Remove the original submesh region and merge in the remeshed version. // You can call this multiple times as the base-triangle-set is updated. // // By default, we allow the submesh to be modified to prevent creation of // non-manifold edges. You can disable this, however then some of the submesh // triangles may be discarded. // // Returns false if there were errors in insertion, ie if some triangles // failed to insert. Does not revert changes that were successful. public bool BackPropropagate(bool bAllowSubmeshRepairs = true) { if (bAllowSubmeshRepairs) { RepairPossibleNonManifoldEdges(); } // remove existing submesh triangles MeshEditor editor = new MeshEditor(BaseMesh); editor.RemoveTriangles(cur_base_tris, true); // insert new submesh int[] new_tris = new int[Region.SubMesh.TriangleCount]; ReinsertSubToBaseMapV = null; bool bOK = editor.ReinsertSubmesh(Region, ref new_tris, out ReinsertSubToBaseMapV); cur_base_tris = new_tris; // assert that new triangles are all valid (goes wrong sometimes??) Debug.Assert(IndexUtil.IndicesCheck(cur_base_tris, BaseMesh.IsTriangle)); return(bOK); }
public bool Apply() { // do a simple fill SimpleHoleFiller simplefill = new SimpleHoleFiller(Mesh, FillLoop); int fill_gid = Mesh.AllocateTriangleGroup(); bool bOK = simplefill.Fill(fill_gid); if (bOK == false) { return(false); } if (FillLoop.Vertices.Length <= 3) { FillTriangles = simplefill.NewTriangles; FillVertices = new int[0]; return(true); } // extract the simple fill mesh as a submesh, via RegionOperator, so we can backsub later HashSet <int> intial_fill_tris = new HashSet <int>(simplefill.NewTriangles); regionop = new RegionOperator(Mesh, simplefill.NewTriangles, (submesh) => { submesh.ComputeTriMaps = true; }); fillmesh = regionop.Region.SubMesh; // for each boundary vertex, compute the exterior angle sum // we will use this to compute gaussian curvature later boundaryv = new HashSet <int>(MeshIterators.BoundaryEdgeVertices(fillmesh)); exterior_angle_sums = new Dictionary <int, double>(); if (IgnoreBoundaryTriangles == false) { foreach (int sub_vid in boundaryv) { double angle_sum = 0; int base_vid = regionop.Region.MapVertexToBaseMesh(sub_vid); foreach (int tid in regionop.BaseMesh.VtxTrianglesItr(base_vid)) { if (intial_fill_tris.Contains(tid) == false) { Index3i et = regionop.BaseMesh.GetTriangle(tid); int idx = IndexUtil.find_tri_index(base_vid, ref et); angle_sum += regionop.BaseMesh.GetTriInternalAngleR(tid, idx); } } exterior_angle_sums[sub_vid] = angle_sum; } } // try to guess a reasonable edge length that will give us enough geometry to work with in simplify pass double loop_mine, loop_maxe, loop_avge, fill_mine, fill_maxe, fill_avge; MeshQueries.EdgeLengthStatsFromEdges(Mesh, FillLoop.Edges, out loop_mine, out loop_maxe, out loop_avge); MeshQueries.EdgeLengthStats(fillmesh, out fill_mine, out fill_maxe, out fill_avge); double remesh_target_len = loop_avge; if (fill_maxe / remesh_target_len > 10) { remesh_target_len = fill_maxe / 10; } //double remesh_target_len = Math.Min(loop_avge, fill_avge / 4); // remesh up to target edge length, ideally gives us some triangles to work with RemesherPro remesh1 = new RemesherPro(fillmesh); remesh1.SmoothSpeedT = 1.0; MeshConstraintUtil.FixAllBoundaryEdges(remesh1); //remesh1.SetTargetEdgeLength(remesh_target_len / 2); // would this speed things up? on large regions? //remesh1.FastestRemesh(); remesh1.SetTargetEdgeLength(remesh_target_len); remesh1.FastestRemesh(); /* * first round: collapse to minimal mesh, while flipping to try to * get to ballpark minimal mesh. We stop these passes as soon as * we have done two rounds where we couldn't do another collapse * * This is the most unstable part of the algorithm because there * are strong ordering effects. maybe we could sort the edges somehow?? */ int zero_collapse_passes = 0; int collapse_passes = 0; while (collapse_passes++ < 20 && zero_collapse_passes < 2) { // collapse pass int NE = fillmesh.MaxEdgeID; int collapses = 0; for (int ei = 0; ei < NE; ++ei) { if (fillmesh.IsEdge(ei) == false || fillmesh.IsBoundaryEdge(ei)) { continue; } Index2i ev = fillmesh.GetEdgeV(ei); bool a_bdry = boundaryv.Contains(ev.a), b_bdry = boundaryv.Contains(ev.b); if (a_bdry && b_bdry) { continue; } int keepv = (a_bdry) ? ev.a : ev.b; int otherv = (keepv == ev.a) ? ev.b : ev.a; Vector3d newv = fillmesh.GetVertex(keepv); if (MeshUtil.CheckIfCollapseCreatesFlip(fillmesh, ei, newv)) { continue; } DMesh3.EdgeCollapseInfo info; MeshResult result = fillmesh.CollapseEdge(keepv, otherv, out info); if (result == MeshResult.Ok) { collapses++; } } if (collapses == 0) { zero_collapse_passes++; } else { zero_collapse_passes = 0; } // flip pass. we flip in these cases: // 1) if angle between current triangles is too small (slightly more than 90 degrees, currently) // 2) if angle between flipped triangles is smaller than between current triangles // 3) if flipped edge length is shorter *and* such a flip won't flip the normal NE = fillmesh.MaxEdgeID; Vector3d n1, n2, on1, on2; for (int ei = 0; ei < NE; ++ei) { if (fillmesh.IsEdge(ei) == false || fillmesh.IsBoundaryEdge(ei)) { continue; } bool do_flip = false; Index2i ev = fillmesh.GetEdgeV(ei); MeshUtil.GetEdgeFlipNormals(fillmesh, ei, out n1, out n2, out on1, out on2); double dot_cur = n1.Dot(n2); double dot_flip = on1.Dot(on2); if (n1.Dot(n2) < 0.1 || dot_flip > dot_cur + MathUtil.Epsilonf) { do_flip = true; } if (do_flip == false) { Index2i otherv = fillmesh.GetEdgeOpposingV(ei); double len_e = fillmesh.GetVertex(ev.a).Distance(fillmesh.GetVertex(ev.b)); double len_flip = fillmesh.GetVertex(otherv.a).Distance(fillmesh.GetVertex(otherv.b)); if (len_flip < len_e) { if (MeshUtil.CheckIfEdgeFlipCreatesFlip(fillmesh, ei) == false) { do_flip = true; } } } if (do_flip) { DMesh3.EdgeFlipInfo info; MeshResult result = fillmesh.FlipEdge(ei, out info); } } } // Sometimes, for some reason, we have a remaining interior vertex (have only ever seen one?) // Try to force removal of such vertices, even if it makes ugly mesh remove_remaining_interior_verts(); // enable/disable passes. bool DO_FLATTER_PASS = true; bool DO_CURVATURE_PASS = OptimizeDevelopability && true; bool DO_AREA_PASS = OptimizeDevelopability && OptimizeTriangles && true; /* * In this pass we repeat the flipping iterations from the previous pass. * * Note that because of the always-flip-if-dot-is-small case (commented), * this pass will frequently not converge, as some number of edges will * be able to flip back and forth (because neither has large enough dot). * This is not ideal, but also, if we remove this behavior, then we * generally get worse fills. This case basically introduces a sort of * randomization factor that lets us escape local minima... * */ HashSet <int> remaining_edges = new HashSet <int>(fillmesh.EdgeIndices()); HashSet <int> updated_edges = new HashSet <int>(); int flatter_passes = 0; int zero_flips_passes = 0; while (flatter_passes++ < 40 && zero_flips_passes < 2 && remaining_edges.Count() > 0 && DO_FLATTER_PASS) { zero_flips_passes++; foreach (int ei in remaining_edges) { if (fillmesh.IsBoundaryEdge(ei)) { continue; } bool do_flip = false; Index2i ev = fillmesh.GetEdgeV(ei); Vector3d n1, n2, on1, on2; MeshUtil.GetEdgeFlipNormals(fillmesh, ei, out n1, out n2, out on1, out on2); double dot_cur = n1.Dot(n2); double dot_flip = on1.Dot(on2); if (flatter_passes < 20 && dot_cur < 0.1) // this check causes oscillatory behavior { do_flip = true; } if (dot_flip > dot_cur + MathUtil.Epsilonf) { do_flip = true; } if (do_flip) { DMesh3.EdgeFlipInfo info; MeshResult result = fillmesh.FlipEdge(ei, out info); if (result == MeshResult.Ok) { zero_flips_passes = 0; add_all_edges(ei, updated_edges); } } } var tmp = remaining_edges; remaining_edges = updated_edges; updated_edges = tmp; updated_edges.Clear(); } int curvature_passes = 0; if (DO_CURVATURE_PASS) { curvatures = new double[fillmesh.MaxVertexID]; foreach (int vid in fillmesh.VertexIndices()) { update_curvature(vid); } remaining_edges = new HashSet <int>(fillmesh.EdgeIndices()); updated_edges = new HashSet <int>(); /* * In this pass we try to minimize gaussian curvature at all the vertices. * This will recover sharp edges, etc, and do lots of good stuff. * However, this pass will not make much progress if we are not already * relatively close to a minimal mesh, so it really relies on the previous * passes getting us in the ballpark. */ while (curvature_passes++ < 40 && remaining_edges.Count() > 0 && DO_CURVATURE_PASS) { foreach (int ei in remaining_edges) { if (fillmesh.IsBoundaryEdge(ei)) { continue; } Index2i ev = fillmesh.GetEdgeV(ei); Index2i ov = fillmesh.GetEdgeOpposingV(ei); int find_other = fillmesh.FindEdge(ov.a, ov.b); if (find_other != DMesh3.InvalidID) { continue; } double total_curv_cur = curvature_metric_cached(ev.a, ev.b, ov.a, ov.b); if (total_curv_cur < MathUtil.ZeroTolerancef) { continue; } DMesh3.EdgeFlipInfo info; MeshResult result = fillmesh.FlipEdge(ei, out info); if (result != MeshResult.Ok) { continue; } double total_curv_flip = curvature_metric_eval(ev.a, ev.b, ov.a, ov.b); bool keep_flip = total_curv_flip < total_curv_cur - MathUtil.ZeroTolerancef; if (keep_flip == false) { result = fillmesh.FlipEdge(ei, out info); } else { update_curvature(ev.a); update_curvature(ev.b); update_curvature(ov.a); update_curvature(ov.b); add_all_edges(ei, updated_edges); } } var tmp = remaining_edges; remaining_edges = updated_edges; updated_edges = tmp; updated_edges.Clear(); } } //System.Console.WriteLine("collapse {0} flatter {1} curvature {2}", collapse_passes, flatter_passes, curvature_passes); /* * In this final pass, we try to improve triangle quality. We flip if * the flipped triangles have better total aspect ratio, and the * curvature doesn't change **too** much. The .DevelopabilityTolerance * parameter determines what is "too much" curvature change. */ if (DO_AREA_PASS) { remaining_edges = new HashSet <int>(fillmesh.EdgeIndices()); updated_edges = new HashSet <int>(); int area_passes = 0; while (remaining_edges.Count() > 0 && area_passes < 20) { area_passes++; foreach (int ei in remaining_edges) { if (fillmesh.IsBoundaryEdge(ei)) { continue; } Index2i ev = fillmesh.GetEdgeV(ei); Index2i ov = fillmesh.GetEdgeOpposingV(ei); int find_other = fillmesh.FindEdge(ov.a, ov.b); if (find_other != DMesh3.InvalidID) { continue; } double total_curv_cur = curvature_metric_cached(ev.a, ev.b, ov.a, ov.b); double a = aspect_metric(ei); if (a > 1) { continue; } DMesh3.EdgeFlipInfo info; MeshResult result = fillmesh.FlipEdge(ei, out info); if (result != MeshResult.Ok) { continue; } double total_curv_flip = curvature_metric_eval(ev.a, ev.b, ov.a, ov.b); bool keep_flip = Math.Abs(total_curv_cur - total_curv_flip) < DevelopabilityTolerance; if (keep_flip == false) { result = fillmesh.FlipEdge(ei, out info); } else { update_curvature(ev.a); update_curvature(ev.b); update_curvature(ov.a); update_curvature(ov.b); add_all_edges(ei, updated_edges); } } var tmp = remaining_edges; remaining_edges = updated_edges; updated_edges = tmp; updated_edges.Clear(); } } regionop.BackPropropagate(); FillTriangles = regionop.CurrentBaseTriangles; FillVertices = regionop.CurrentBaseInteriorVertices().ToArray(); return(true); }
public MeshResult CollapseEdge(int vKeep, int vRemove, out EdgeCollapseInfo collapse) { collapse = new EdgeCollapseInfo(); if (IsVertex(vKeep) == false || IsVertex(vRemove) == false) { return(MeshResult.Failed_NotAnEdge); } int b = vKeep; // renaming for sanity. We remove a and keep b int a = vRemove; List <int> edges_b = vertex_edges[b]; int eab = Find_edge(a, b); if (eab == InvalidID) { return(MeshResult.Failed_NotAnEdge); } int t0 = edges[4 * eab + 2]; if (t0 == InvalidID) { return(MeshResult.Failed_BrokenTopology); } Index3i T0tv = GetTriangle(t0); int c = IndexUtil.find_tri_other_vtx(a, b, T0tv); // look up opposing triangle/vtx if we are not in boundary case bool bIsBoundaryEdge = false; int d = InvalidID; int t1 = edges[4 * eab + 3]; if (t1 != InvalidID) { Index3i T1tv = GetTriangle(t1); d = IndexUtil.find_tri_other_vtx(a, b, T1tv); if (c == d) { return(MeshResult.Failed_FoundDuplicateTriangle); } } else { bIsBoundaryEdge = true; } // We cannot collapse if edge lists of a and b share vertices other // than c and d (because then we will make a triangle [x b b]. // Unfortunately I cannot see a way to do this more efficiently than brute-force search // [TODO] if we had tri iterator for a, couldn't we check each tri for b (skipping t0 and t1) ? List <int> edges_a = vertex_edges[a]; int edges_a_count = 0; foreach (int eid_a in edges_a) { int vax = Edge_other_v(eid_a, a); edges_a_count++; if (vax == b || vax == c || vax == d) { continue; } foreach (int eid_b in edges_b) { if (Edge_other_v(eid_b, b) == vax) { return(MeshResult.Failed_InvalidNeighbourhood); } } } // We cannot collapse if we have a tetrahedron. In this case a has 3 nbr edges, // and edge cd exists. But that is not conclusive, we also have to check that // cd is an internal edge, and that each of its tris contain a or b if (edges_a_count == 3 && bIsBoundaryEdge == false) { int edc = Find_edge(d, c); int edc_i = 4 * edc; if (edc != InvalidID && edges[edc_i + 3] != InvalidID) { int edc_t0 = edges[edc_i + 2]; int edc_t1 = edges[edc_i + 3]; if ((Tri_has_v(edc_t0, a) && Tri_has_v(edc_t1, b)) || (Tri_has_v(edc_t0, b) && Tri_has_v(edc_t1, a))) { return(MeshResult.Failed_CollapseTetrahedron); } } } else if (edges_a_count == 2 && bIsBoundaryEdge == true) { // cannot collapse edge if we are down to a single triangle if (edges_b.Count == 2 && vertex_edges[c].Count == 2) { return(MeshResult.Failed_CollapseTriangle); } } // [RMS] this was added from C++ version...seems like maybe I never hit // this case? Conceivably could be porting bug but looking at the // C++ code I cannot see how we could possibly have caught this case... // // cannot collapse an edge where both vertices are boundary vertices // because that would create a bowtie // // NOTE: potentially scanning all edges here...couldn't we // pick up eac/bc/ad/bd as we go? somehow? if (bIsBoundaryEdge == false && Vertex_is_boundary(a) && Vertex_is_boundary(b)) { return(MeshResult.Failed_InvalidNeighbourhood); } // 1) remove edge ab from vtx b // 2) find edges ad and ac, and tris tad, tac across those edges (will use later) // 3) for other edges, replace a with b, and add that edge to b // 4) replace a with b in all triangles connected to a int ead = InvalidID, eac = InvalidID; int tad = InvalidID, tac = InvalidID; foreach (int eid in edges_a) { int o = Edge_other_v(eid, a); if (o == b) { if (edges_b.Remove(eid) != true) { Debug_fail("remove case o == b"); } } else if (o == c) { eac = eid; if (vertex_edges[c].Remove(eid) != true) { Debug_fail("remove case o == c"); } tac = Edge_other_t(eid, t0); } else if (o == d) { ead = eid; if (vertex_edges[d].Remove(eid) != true) { Debug_fail("remove case o == c, step 1"); } tad = Edge_other_t(eid, t1); } else { if (Replace_edge_vertex(eid, a, b) == -1) { Debug_fail("remove case else"); } edges_b.Add(eid); } // [TODO] perhaps we can already have unique tri list because of the manifold-nbrhood check we need to do... for (int j = 0; j < 2; ++j) { int t_j = edges[4 * eid + 2 + j]; if (t_j != InvalidID && t_j != t0 && t_j != t1) { if (Tri_has_v(t_j, a)) { if (Replace_tri_vertex(t_j, a, b) == -1) { Debug_fail("remove last check"); } vertices_refcount.Increment(b); vertices_refcount.Decrement(a); } } } } int ebc = InvalidID, ebd = InvalidID; if (bIsBoundaryEdge == false) { // remove all edges from vtx a, then remove vtx a edges_a.Clear(); Debug.Assert(vertices_refcount.RefCount(a) == 3); // in t0,t1, and initial ref vertices_refcount.Decrement(a, 3); Debug.Assert(vertices_refcount.IsValid(a) == false); // remove triangles T0 and T1, and update b/c/d refcounts triangles_refcount.Decrement(t0); triangles_refcount.Decrement(t1); vertices_refcount.Decrement(c); vertices_refcount.Decrement(d); vertices_refcount.Decrement(b, 2); Debug.Assert(triangles_refcount.IsValid(t0) == false); Debug.Assert(triangles_refcount.IsValid(t1) == false); // remove edges ead, eab, eac edges_refcount.Decrement(ead); edges_refcount.Decrement(eab); edges_refcount.Decrement(eac); Debug.Assert(edges_refcount.IsValid(ead) == false); Debug.Assert(edges_refcount.IsValid(eab) == false); Debug.Assert(edges_refcount.IsValid(eac) == false); // replace t0 and t1 in edges ebd and ebc that we kept ebd = Find_edge(b, d); ebc = Find_edge(b, c); if (Replace_edge_triangle(ebd, t1, tad) == -1) { Debug_fail("isboundary=false branch, ebd replace triangle"); } if (Replace_edge_triangle(ebc, t0, tac) == -1) { Debug_fail("isboundary=false branch, ebc replace triangle"); } // update tri-edge-nbrs in tad and tac if (tad != InvalidID) { if (Replace_triangle_edge(tad, ead, ebd) == -1) { Debug_fail("isboundary=false branch, ebd replace triangle"); } } if (tac != InvalidID) { if (Replace_triangle_edge(tac, eac, ebc) == -1) { Debug_fail("isboundary=false branch, ebd replace triangle"); } } } else { // this is basically same code as above, just not referencing t1/d // remove all edges from vtx a, then remove vtx a edges_a.Clear(); Debug.Assert(vertices_refcount.RefCount(a) == 2); // in t0 and initial ref vertices_refcount.Decrement(a, 2); Debug.Assert(vertices_refcount.IsValid(a) == false); // remove triangle T0 and update b/c refcounts triangles_refcount.Decrement(t0); vertices_refcount.Decrement(c); vertices_refcount.Decrement(b); Debug.Assert(triangles_refcount.IsValid(t0) == false); // remove edges eab and eac edges_refcount.Decrement(eab); edges_refcount.Decrement(eac); Debug.Assert(edges_refcount.IsValid(eab) == false); Debug.Assert(edges_refcount.IsValid(eac) == false); // replace t0 in edge ebc that we kept ebc = Find_edge(b, c); if (Replace_edge_triangle(ebc, t0, tac) == -1) { Debug_fail("isboundary=false branch, ebc replace triangle"); } // update tri-edge-nbrs in tac if (tac != InvalidID) { if (Replace_triangle_edge(tac, eac, ebc) == -1) { Debug_fail("isboundary=true branch, ebd replace triangle"); } } } collapse.vKept = vKeep; collapse.vRemoved = vRemove; collapse.bIsBoundary = bIsBoundaryEdge; collapse.eCollapsed = eab; collapse.tRemoved0 = t0; collapse.tRemoved1 = t1; collapse.eRemoved0 = eac; collapse.eRemoved1 = ead; collapse.eKept0 = ebc; collapse.eKept1 = ebd; UpdateTimeStamp(true); return(MeshResult.Ok); }
public MeshResult SplitEdge(int eab, out EdgeSplitInfo split) { split = new EdgeSplitInfo(); if (!IsEdge(eab)) { return(MeshResult.Failed_NotAnEdge); } // look up primary edge & triangle int eab_i = 4 * eab; int a = edges[eab_i], b = edges[eab_i + 1]; int t0 = edges[eab_i + 2]; if (t0 == InvalidID) { return(MeshResult.Failed_BrokenTopology); } Index3i T0tv = GetTriangle(t0); int[] T0tv_array = T0tv.array; int c = IndexUtil.orient_tri_edge_and_find_other_vtx(ref a, ref b, T0tv_array); // create new vertex Vector3D vNew = 0.5 * (GetVertex(a) + GetVertex(b)); int f = AppendVertex(vNew); if (HasVertexNormals) { SetVertexNormal(f, (GetVertexNormal(a) + GetVertexNormal(b)).Normalized); } if (HasVertexColors) { SetVertexColor(f, 0.5f * (GetVertexColor(a) + GetVertexColor(b))); } if (HasVertexUVs) { SetVertexUV(f, 0.5f * (GetVertexUV(a) + GetVertexUV(b))); } // quite a bit of code is duplicated between boundary and non-boundary case, but it // is too hard to follow later if we factor it out... if (Edge_is_boundary(eab)) { // look up edge bc, which needs to be modified Index3i T0te = GetTriEdges(t0); int ebc = T0te[IndexUtil.find_edge_index_in_tri(b, c, T0tv_array)]; // rewrite existing triangle Replace_tri_vertex(t0, b, f); // add new second triangle int t2 = Add_triangle_only(f, b, c, InvalidID, InvalidID, InvalidID); if (triangle_groups != null) { triangle_groups.Insert(triangle_groups[t0], t2); } // rewrite edge bc, create edge af Replace_edge_triangle(ebc, t0, t2); int eaf = eab; Replace_edge_vertex(eaf, b, f); vertex_edges[b].Remove(eab); vertex_edges[f].Add(eaf); // create new edges fb and fc int efb = Add_edge(f, b, t2); int efc = Add_edge(f, c, t0, t2); // update triangle edge-nbrs Replace_triangle_edge(t0, ebc, efc); Set_triangle_edges(t2, efb, ebc, efc); // update vertex refcounts vertices_refcount.Increment(c); vertices_refcount.Increment(f, 2); split.bIsBoundary = true; split.vNew = f; split.eNewBN = efb; split.eNewCN = efc; split.eNewDN = InvalidID; UpdateTimeStamp(true); return(MeshResult.Ok); } else // interior triangle branch // look up other triangle { int t1 = edges[eab_i + 3]; Index3i T1tv = GetTriangle(t1); int[] T1tv_array = T1tv.array; int d = IndexUtil.find_tri_other_vtx(a, b, T1tv_array); // look up edges that we are going to need to update // [TODO OPT] could use ordering to reduce # of compares here Index3i T0te = GetTriEdges(t0); int ebc = T0te[IndexUtil.find_edge_index_in_tri(b, c, T0tv_array)]; Index3i T1te = GetTriEdges(t1); int edb = T1te[IndexUtil.find_edge_index_in_tri(d, b, T1tv_array)]; // rewrite existing triangles Replace_tri_vertex(t0, b, f); Replace_tri_vertex(t1, b, f); // add two new triangles to close holes we just created int t2 = Add_triangle_only(f, b, c, InvalidID, InvalidID, InvalidID); int t3 = Add_triangle_only(f, d, b, InvalidID, InvalidID, InvalidID); if (triangle_groups != null) { triangle_groups.Insert(triangle_groups[t0], t2); triangle_groups.Insert(triangle_groups[t1], t3); } // update the edges we found above, to point to new triangles Replace_edge_triangle(ebc, t0, t2); Replace_edge_triangle(edb, t1, t3); // edge eab became eaf int eaf = eab; //Edge * eAF = eAB; Replace_edge_vertex(eaf, b, f); // update a/b/f vertex-edges vertex_edges[b].Remove(eab); vertex_edges[f].Add(eaf); // create new edges connected to f (also updates vertex-edges) int efb = Add_edge(f, b, t2, t3); int efc = Add_edge(f, c, t0, t2); int edf = Add_edge(d, f, t1, t3); // update triangle edge-nbrs Replace_triangle_edge(t0, ebc, efc); Replace_triangle_edge(t1, edb, edf); Set_triangle_edges(t2, efb, ebc, efc); Set_triangle_edges(t3, edf, edb, efb); // update vertex refcounts vertices_refcount.Increment(c); vertices_refcount.Increment(d); vertices_refcount.Increment(f, 4); split.bIsBoundary = false; split.vNew = f; split.eNewBN = efb; split.eNewCN = efc; split.eNewDN = edf; UpdateTimeStamp(true); return(MeshResult.Ok); } }
/// <summary> // This function checks that the mesh is well-formed, ie all internal data // structures are consistent /// </summary> public bool CheckValidity(bool bAllowNonManifoldVertices = false, FailMode eFailMode = FailMode.Throw) { int[] triToVtxRefs = new int[this.MaxVertexID]; bool is_ok = true; Action <bool> CheckOrFailF = (b) => { is_ok = is_ok && b; }; if (eFailMode == FailMode.DebugAssert) { CheckOrFailF = (b) => { Debug.Assert(b); is_ok = is_ok && b; }; } else if (eFailMode == FailMode.gDevAssert) { CheckOrFailF = (b) => { Util.gDevAssert(b); is_ok = is_ok && b; }; } else if (eFailMode == FailMode.Throw) { CheckOrFailF = (b) => { if (b == false) { throw new Exception("DMesh3.CheckValidity: check failed"); } }; } if (normals != null) { CheckOrFailF(normals.Size == vertices.Size); } if (colors != null) { CheckOrFailF(colors.Size == vertices.Size); } if (uv != null) { CheckOrFailF(uv.Size / 2 == vertices.Size / 3); } if (triangle_groups != null) { CheckOrFailF(triangle_groups.Size == triangles.Size / 3); } foreach (int tID in TriangleIndices()) { CheckOrFailF(IsTriangle(tID)); CheckOrFailF(triangles_refcount.RefCount(tID) == 1); // vertices must exist Index3i tv = GetTriangle(tID); for (int j = 0; j < 3; ++j) { CheckOrFailF(IsVertex(tv[j])); triToVtxRefs[tv[j]] += 1; } // edges must exist and reference this tri Index3i e = new Index3i(); for (int j = 0; j < 3; ++j) { int a = tv[j], b = tv[(j + 1) % 3]; e[j] = FindEdge(a, b); CheckOrFailF(e[j] != InvalidID); CheckOrFailF(Edge_has_t(e[j], tID)); CheckOrFailF(e[j] == FindEdgeFromTri(a, b, tID)); } CheckOrFailF(e[0] != e[1] && e[0] != e[2] && e[1] != e[2]); // tri nbrs must exist and reference this tri, or same edge must be boundary edge Index3i te = GetTriEdges(tID); for (int j = 0; j < 3; ++j) { int eid = te[j]; CheckOrFailF(IsEdge(eid)); int tOther = Edge_other_t(eid, tID); if (tOther == InvalidID) { CheckOrFailF(Tri_is_boundary(tID)); continue; } CheckOrFailF(Tri_has_neighbour_t(tOther, tID) == true); // edge must have same two verts as tri for same index int a = tv[j], b = tv[(j + 1) % 3]; Index2i ev = GetEdgeV(te[j]); CheckOrFailF(IndexUtil.same_pair_unordered(a, b, ev[0], ev[1])); // also check that nbr edge has opposite orientation Index3i othertv = GetTriangle(tOther); int found = IndexUtil.find_tri_ordered_edge(b, a, othertv.array); CheckOrFailF(found != InvalidID); } } // edge verts/tris must exist foreach (int eID in EdgeIndices()) { CheckOrFailF(IsEdge(eID)); CheckOrFailF(edges_refcount.RefCount(eID) == 1); Index2i ev = GetEdgeV(eID); Index2i et = GetEdgeT(eID); CheckOrFailF(IsVertex(ev[0])); CheckOrFailF(IsVertex(ev[1])); CheckOrFailF(et[0] != InvalidID); CheckOrFailF(ev[0] < ev[1]); CheckOrFailF(IsTriangle(et[0])); if (et[1] != InvalidID) { CheckOrFailF(IsTriangle(et[1])); } } // verify compact check bool is_compact = vertices_refcount.Is_dense; if (is_compact) { for (int vid = 0; vid < vertices.Length / 3; ++vid) { CheckOrFailF(vertices_refcount.IsValid(vid)); } } // vertex edges must exist and reference this vert foreach (int vID in VertexIndices()) { CheckOrFailF(IsVertex(vID)); Vector3D v = GetVertex(vID); CheckOrFailF(double.IsNaN(v.LengthSquared) == false); CheckOrFailF(double.IsInfinity(v.LengthSquared) == false); List <int> l = vertex_edges[vID]; foreach (int edgeid in l) { CheckOrFailF(IsEdge(edgeid)); CheckOrFailF(Edge_has_v(edgeid, vID)); int otherV = Edge_other_v(edgeid, vID); int e2 = Find_edge(vID, otherV); CheckOrFailF(e2 != InvalidID); CheckOrFailF(e2 == edgeid); e2 = Find_edge(otherV, vID); CheckOrFailF(e2 != InvalidID); CheckOrFailF(e2 == edgeid); } List <int> vTris = new List <int>(), vTris2 = new List <int>(); GetVtxTriangles(vID, vTris, false); GetVtxTriangles(vID, vTris2, true); CheckOrFailF(vTris.Count == vTris2.Count); //System.Console.WriteLine(string.Format("{0} {1} {2}", vID, vTris.Count, GetVtxEdges(vID).Count)); if (bAllowNonManifoldVertices) { CheckOrFailF(vTris.Count <= GetVtxEdges(vID).Count); } else { CheckOrFailF(vTris.Count == GetVtxEdges(vID).Count || vTris.Count == GetVtxEdges(vID).Count - 1); } CheckOrFailF(vertices_refcount.RefCount(vID) == vTris.Count + 1); CheckOrFailF(triToVtxRefs[vID] == vTris.Count); foreach (int tID in vTris) { CheckOrFailF(Tri_has_v(tID, vID)); } // check that edges around vert only references tris above, and reference all of them! List <int> vRemoveTris = new List <int>(vTris); foreach (int edgeid in l) { Index2i edget = GetEdgeT(edgeid); CheckOrFailF(vTris.Contains(edget[0])); if (edget[1] != InvalidID) { CheckOrFailF(vTris.Contains(edget[1])); } vRemoveTris.Remove(edget[0]); if (edget[1] != InvalidID) { vRemoveTris.Remove(edget[1]); } } CheckOrFailF(vRemoveTris.Count == 0); } return(is_ok); }
public MeshResult FlipEdge(int eab, out EdgeFlipInfo flip) { flip = new EdgeFlipInfo(); if (!IsEdge(eab)) { return(MeshResult.Failed_NotAnEdge); } if (Edge_is_boundary(eab)) { return(MeshResult.Failed_IsBoundaryEdge); } // find oriented edge [a,b], tris t0,t1, and other verts c in t0, d in t1 int eab_i = 4 * eab; int a = edges[eab_i], b = edges[eab_i + 1]; int t0 = edges[eab_i + 2], t1 = edges[eab_i + 3]; int[] T0tv = GetTriangle(t0).array; int[] T1tv = GetTriangle(t1).array; int c = IndexUtil.orient_tri_edge_and_find_other_vtx(ref a, ref b, T0tv); int d = IndexUtil.find_tri_other_vtx(a, b, T1tv); if (c == InvalidID || d == InvalidID) { return(MeshResult.Failed_BrokenTopology); } int flipped = Find_edge(c, d); if (flipped != InvalidID) { return(MeshResult.Failed_FlippedEdgeExists); } // find edges bc, ca, ad, db int ebc = Find_tri_neighbour_edge(t0, b, c); int eca = Find_tri_neighbour_edge(t0, c, a); int ead = Find_tri_neighbour_edge(t1, a, d); int edb = Find_tri_neighbour_edge(t1, d, b); // update triangles Set_triangle(t0, c, d, b); Set_triangle(t1, d, c, a); // update edge AB, which becomes flipped edge CD Set_edge_vertices(eab, c, d); Set_edge_triangles(eab, t0, t1); int ecd = eab; // update the two other edges whose triangle nbrs have changed if (Replace_edge_triangle(eca, t0, t1) == -1) { throw new ArgumentException("DMesh3.FlipEdge: first replace_edge_triangle failed"); } if (Replace_edge_triangle(edb, t1, t0) == -1) { throw new ArgumentException("DMesh3.FlipEdge: second replace_edge_triangle failed"); } // update triangle nbr lists (these are edges) Set_triangle_edges(t0, ecd, edb, ebc); Set_triangle_edges(t1, ecd, eca, ead); // remove old eab from verts a and b, and decrement ref counts if (vertex_edges[a].Remove(eab) == false) { throw new ArgumentException("DMesh3.FlipEdge: first vertex_edges remove failed"); } if (vertex_edges[b].Remove(eab) == false) { throw new ArgumentException("DMesh3.FlipEdge: second vertex_edges remove failed"); } vertices_refcount.Decrement(a); vertices_refcount.Decrement(b); if (IsVertex(a) == false || IsVertex(b) == false) { throw new ArgumentException("DMesh3.FlipEdge: either a or b is not a vertex?"); } // add new edge ecd to verts c and d, and increment ref counts vertex_edges[c].Add(ecd); vertex_edges[d].Add(ecd); vertices_refcount.Increment(c); vertices_refcount.Increment(d); // success! collect up results flip.eID = eab; flip.v0 = a; flip.v1 = b; flip.ov0 = c; flip.ov1 = d; flip.t0 = t0; flip.t1 = t1; UpdateTimeStamp(true); return(MeshResult.Ok); }
protected virtual ProcessResult ProcessEdge(int edgeID) { RuntimeDebugCheck(edgeID); EdgeConstraint constraint = (constraints == null) ? EdgeConstraint.Unconstrained : constraints.GetEdgeConstraint(edgeID); if (constraint.NoModifications) { return(ProcessResult.Ignored_EdgeIsFullyConstrained); } // look up verts and tris for this edge int a = 0, b = 0, t0 = 0, t1 = 0; if (mesh.GetEdge(edgeID, ref a, ref b, ref t0, ref t1) == false) { return(ProcessResult.Failed_NotAnEdge); } bool bIsBoundaryEdge = (t1 == NGonsCore.geometry3Sharp.mesh.DMesh3.InvalidID); // look up 'other' verts c (from t0) and d (from t1, if it exists) Index3i T0tv = mesh.GetTriangle(t0); int c = IndexUtil.find_tri_other_vtx(a, b, T0tv); Index3i T1tv = (bIsBoundaryEdge) ? NGonsCore.geometry3Sharp.mesh.DMesh3.InvalidTriangle : mesh.GetTriangle(t1); int d = (bIsBoundaryEdge) ? NGonsCore.geometry3Sharp.mesh.DMesh3.InvalidID : IndexUtil.find_tri_other_vtx(a, b, T1tv); Vector3D vA = mesh.GetVertex(a); Vector3D vB = mesh.GetVertex(b); double edge_len_sqr = (vA - vB).LengthSquared; begin_collapse(); // check if we should collapse, and also find which vertex we should collapse to, // in cases where we have constraints/etc int collapse_to = -1; bool bCanCollapse = EnableCollapses && constraint.CanCollapse && edge_len_sqr < MinEdgeLength * MinEdgeLength && can_collapse_constraints(edgeID, a, b, c, d, t0, t1, out collapse_to); // optimization: if edge cd exists, we cannot collapse or flip. look that up here? // funcs will do it internally... // (or maybe we can collapse if cd exists? edge-collapse doesn't check for it explicitly...) // if edge length is too short, we want to collapse it bool bTriedCollapse = false; if (bCanCollapse) { int iKeep = b, iCollapse = a; Vector3D vNewPos = (vA + vB) * 0.5; // if either vtx is fixed, collapse to that position if (collapse_to == b) { vNewPos = vB; } else if (collapse_to == a) { iKeep = a; iCollapse = b; vNewPos = vA; } else { vNewPos = get_projected_collapse_position(iKeep, vNewPos); } // TODO be smart about picking b (keep vtx). // - swap if one is bdry vtx, for example? // lots of cases where we cannot collapse, but we should just let // mesh sort that out, right? COUNT_COLLAPSES++; NGonsCore.geometry3Sharp.mesh.DMesh3.EdgeCollapseInfo collapseInfo; MeshResult result = mesh.CollapseEdge(iKeep, iCollapse, out collapseInfo); if (result == MeshResult.Ok) { mesh.SetVertex(b, vNewPos); if (constraints != null) { constraints.ClearEdgeConstraint(edgeID); constraints.ClearEdgeConstraint(collapseInfo.eRemoved0); if (collapseInfo.eRemoved1 != NGonsCore.geometry3Sharp.mesh.DMesh3.InvalidID) { constraints.ClearEdgeConstraint(collapseInfo.eRemoved1); } constraints.ClearVertexConstraint(iCollapse); } OnEdgeCollapse(edgeID, iKeep, iCollapse, collapseInfo); DoDebugChecks(); return(ProcessResult.Ok_Collapsed); } else { bTriedCollapse = true; } } end_collapse(); begin_flip(); // if this is not a boundary edge, maybe we want to flip bool bTriedFlip = false; if (EnableFlips && constraint.CanFlip && bIsBoundaryEdge == false) { // don't want to flip if it will invert triangle...tetrahedron sign?? // can we do this more efficiently somehow? bool a_is_boundary_vtx = (MeshIsClosed) ? false : (bIsBoundaryEdge || mesh.Vertex_is_boundary(a)); bool b_is_boundary_vtx = (MeshIsClosed) ? false : (bIsBoundaryEdge || mesh.Vertex_is_boundary(b)); bool c_is_boundary_vtx = (MeshIsClosed) ? false : mesh.Vertex_is_boundary(c); bool d_is_boundary_vtx = (MeshIsClosed) ? false : mesh.Vertex_is_boundary(d); int valence_a = mesh.GetVtxEdgeValence(a), valence_b = mesh.GetVtxEdgeValence(b); int valence_c = mesh.GetVtxEdgeValence(c), valence_d = mesh.GetVtxEdgeValence(d); int valence_a_target = (a_is_boundary_vtx) ? valence_a : 6; int valence_b_target = (b_is_boundary_vtx) ? valence_b : 6; int valence_c_target = (c_is_boundary_vtx) ? valence_c : 6; int valence_d_target = (d_is_boundary_vtx) ? valence_d : 6; // if total valence error improves by flip, we want to do it int curr_err = Math.Abs(valence_a - valence_a_target) + Math.Abs(valence_b - valence_b_target) + Math.Abs(valence_c - valence_c_target) + Math.Abs(valence_d - valence_d_target); int flip_err = Math.Abs((valence_a - 1) - valence_a_target) + Math.Abs((valence_b - 1) - valence_b_target) + Math.Abs((valence_c + 1) - valence_c_target) + Math.Abs((valence_d + 1) - valence_d_target); if (flip_err < curr_err) { // try flip NGonsCore.geometry3Sharp.mesh.DMesh3.EdgeFlipInfo flipInfo; COUNT_FLIPS++; MeshResult result = mesh.FlipEdge(edgeID, out flipInfo); if (result == MeshResult.Ok) { DoDebugChecks(); return(ProcessResult.Ok_Flipped); } else { bTriedFlip = true; } } } end_flip(); begin_split(); // if edge length is too long, we want to split it bool bTriedSplit = false; if (EnableSplits && constraint.CanSplit && edge_len_sqr > MaxEdgeLength * MaxEdgeLength) { NGonsCore.geometry3Sharp.mesh.DMesh3.EdgeSplitInfo splitInfo; COUNT_SPLITS++; MeshResult result = mesh.SplitEdge(edgeID, out splitInfo); if (result == MeshResult.Ok) { update_after_split(edgeID, a, b, splitInfo); OnEdgeSplit(edgeID, a, b, splitInfo); DoDebugChecks(); return(ProcessResult.Ok_Split); } else { bTriedSplit = true; } } end_split(); if (bTriedFlip || bTriedSplit || bTriedCollapse) { return(ProcessResult.Failed_OpNotSuccessful); } else { return(ProcessResult.Ignored_EdgeIsFine); } }
DMesh3 ComputeInflation(DMesh3 planarMesh) { DMesh3 mesh = new DMesh3(planarMesh); DijkstraGraphDistance dist = new DijkstraGraphDistance( mesh.MaxVertexID, false, (vid) => { return(mesh.IsVertex(vid)); }, (a, b) => { return((float)mesh.GetVertex(a).Distance(mesh.GetVertex(b))); }, mesh.VtxVerticesItr); foreach (int vid in MeshIterators.BoundaryVertices(mesh)) { dist.AddSeed(vid, 0); } dist.Compute(); float max_dist = dist.MaxDistance; float[] distances = new float[mesh.MaxVertexID]; foreach (int vid in mesh.VertexIndices()) { distances[vid] = dist.GetDistance(vid); } List <int> local_maxima = new List <int>(); foreach (int vid in MeshIterators.InteriorVertices(mesh)) { float d = distances[vid]; bool is_maxima = true; foreach (int nbrid in mesh.VtxVerticesItr(vid)) { if (distances[nbrid] > d) { is_maxima = false; } } if (is_maxima) { local_maxima.Add(vid); } } // smooth distances (really should use cotan here!!) float smooth_alpha = 0.1f; int smooth_rounds = 5; foreach (int ri in Interval1i.Range(smooth_rounds)) { foreach (int vid in mesh.VertexIndices()) { float cur = distances[vid]; float centroid = 0; int nbr_count = 0; foreach (int nbrid in mesh.VtxVerticesItr(vid)) { centroid += distances[nbrid]; nbr_count++; } centroid /= nbr_count; distances[vid] = (1 - smooth_alpha) * cur + (smooth_alpha) * centroid; } } Vector3d normal = Vector3d.AxisZ; foreach (int vid in mesh.VertexIndices()) { if (dist.IsSeed(vid)) { continue; } float h = distances[vid]; // [RMS] there are different options here... h = 2 * (float)Math.Sqrt(h); float offset = h; Vector3d d = mesh.GetVertex(vid); mesh.SetVertex(vid, d + (Vector3d)(offset * normal)); } DMesh3 compacted = new DMesh3(); var compactInfo = compacted.CompactCopy(mesh); IndexUtil.Apply(local_maxima, compactInfo.MapV); mesh = compacted; MeshVertexSelection boundary_sel = new MeshVertexSelection(mesh); HashSet <int> boundaryV = new HashSet <int>(MeshIterators.BoundaryVertices(mesh)); boundary_sel.Select(boundaryV); boundary_sel.ExpandToOneRingNeighbours(); LaplacianMeshSmoother smoother = new LaplacianMeshSmoother(mesh); foreach (int vid in boundary_sel) { if (boundaryV.Contains(vid)) { smoother.SetConstraint(vid, mesh.GetVertex(vid), 100.0f, true); } else { smoother.SetConstraint(vid, mesh.GetVertex(vid), 10.0f, false); } } foreach (int vid in local_maxima) { smoother.SetConstraint(vid, mesh.GetVertex(vid), 50, false); } bool ok = smoother.SolveAndUpdateMesh(); Util.gDevAssert(ok); List <int> intVerts = new List <int>(MeshIterators.InteriorVertices(mesh)); MeshIterativeSmooth smooth = new MeshIterativeSmooth(mesh, intVerts.ToArray(), true); smooth.SmoothType = MeshIterativeSmooth.SmoothTypes.Cotan; smooth.Rounds = 10; smooth.Alpha = 0.1f; smooth.Smooth(); return(mesh); }