Ejemplo n.º 1
0
        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));
            }
        }
Ejemplo n.º 2
0
        /// <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);
        }
Ejemplo n.º 3
0
        /// <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);
        }
Ejemplo n.º 4
0
        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));
        }
Ejemplo n.º 5
0
        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();
                }
            }
        }
Ejemplo n.º 6
0
        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);
        }
Ejemplo n.º 7
0
        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);
        }
Ejemplo n.º 8
0
        // 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]));
        }
Ejemplo n.º 9
0
 /// <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);
     }
 }
Ejemplo n.º 10
0
        /// <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);
        }
Ejemplo n.º 11
0
        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>()));
        }
Ejemplo n.º 12
0
        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>()));
        }
Ejemplo n.º 13
0
        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);
        }
Ejemplo n.º 14
0
        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);
            }
        }
Ejemplo n.º 15
0
        // 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);
            }
        }
Ejemplo n.º 16
0
        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,
            });
        }
Ejemplo n.º 17
0
        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);
        }
Ejemplo n.º 18
0
        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,
            });
        }
Ejemplo n.º 19
0
        // 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);
        }
Ejemplo n.º 20
0
        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);
        }
Ejemplo n.º 21
0
        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);
        }
Ejemplo n.º 22
0
        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);
            }
        }
Ejemplo n.º 23
0
        /// <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);
        }
Ejemplo n.º 24
0
        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);
        }
Ejemplo n.º 25
0
        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);
            }
        }
Ejemplo n.º 26
0
        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);
        }