protected override void ApplyCurrentStamp(Frame3f vCenter, int tid, DijkstraGraphDistance dj, VectorDisplacement map)
        {
            Vector3d n = Mesh.GetTriNormal(tid);

            List <int> order = dj.GetOrder();
            int        N     = order.Count;

            for (int k = 0; k < N; ++k)
            {
                int vid = order[k];
                //double d = dj.GetDistance(vid);
                double d = (Mesh.GetVertex(vid) - vCenter.Origin).Length;
                double t = MathUtil.Clamp(d / Radius, 0.0, 1.0);
                t = MathUtil.WyvillFalloff01(t);
                Vector3d offset = Power * t * n;
                if (Invert)
                {
                    map[vid] = map[vid] - offset;
                }
                else
                {
                    map[vid] = map[vid] + offset;
                }
            }
        }
Exemple #2
0
        public static void test_dijkstra()
        {
            DMesh3 mesh = TestUtil.LoadTestInputMesh("bunny_solid.obj");

            int max_y = 0;

            foreach (int vid in mesh.VertexIndices())
            {
                if (mesh.GetVertex(vid).y > mesh.GetVertex(max_y).y)
                {
                    max_y = vid;
                }
            }
            Func <int, int, float> VertexDistanceF = (v1, v2) => {
                return((float)mesh.GetVertex(v1).Distance(mesh.GetVertex(v2)));
            };

            DijkstraGraphDistance dist = new DijkstraGraphDistance(mesh.MaxVertexID, false,
                                                                   mesh.IsVertex, VertexDistanceF, mesh.VtxVerticesItr);

            dist.AddSeed(max_y, 0);
            dist.Compute();
            TestUtil.SetColorsFromScalarF(mesh, dist.GetDistance, new Vector2f(0, dist.MaxDistance));
            TestUtil.WriteTestOutputMeshes(new List <IMesh>()
            {
                mesh, TestUtil.MakeMarker(mesh.GetVertex(max_y), 1, Colorf.Red)
            },
                                           "dijkstra_colormap.obj", false, true);
        }
Exemple #3
0
        public static EdgeSpan find_edge_path(DMesh3 mesh, int v0, int v1)
        {
            if (v0 == v1)
            {
                throw new Exception("same vertices!");
            }

            int eid = mesh.FindEdge(v0, v1);

            if (eid >= 0)
            {
                return(EdgeSpan.FromEdges(mesh, new List <int>()
                {
                    eid
                }));
            }

            DijkstraGraphDistance dist = DijkstraGraphDistance.MeshVertices(mesh);

            //DijkstraGraphDistance dist = DijkstraGraphDistance.MeshVerticesSparse(mesh);
            dist.AddSeed(v0, 0);
            dist.ComputeToNode(v1);
            List <int> vpath = new List <int>();
            bool       bOK   = dist.GetPathToSeed(v1, vpath);

            Util.gDevAssert(bOK);
            vpath.Reverse();
            return(EdgeSpan.FromVertices(mesh, vpath));
        }
        public override void Apply(Frame3f vNextPos, int tid)
        {
            if (MapSourceF == null)
            {
                f3.DebugUtil.Log("[VectorDisplacementBaseBrush] .MapSourceF is null!");
                return;
            }
            VectorDisplacement use_map = MapSourceF();

            if (use_map == null || use_map.Count != Mesh.MaxVertexID)
            {
                return;
            }

            DijkstraGraphDistance dj = Dijkstra;

            dj.Reset();

            Index3i  ti = Mesh.GetTriangle(tid);
            Vector3d c  = Mesh.GetTriCentroid(tid);

            dj.AddSeed(ti.a, (float)Mesh.GetVertex(ti.a).Distance(c));
            dj.AddSeed(ti.b, (float)Mesh.GetVertex(ti.b).Distance(c));
            dj.AddSeed(ti.c, (float)Mesh.GetVertex(ti.c).Distance(c));
            double compute_Dist = MathUtil.SqrtTwo * Radius;

            dj.ComputeToMaxDistance((float)compute_Dist);

            ApplyCurrentStamp(vNextPos, tid, dj, use_map);

            mesh_changed();
        }
        protected override void ApplyCurrentStamp(Frame3f vCenter, int tid, DijkstraGraphDistance dj, VectorDisplacement map)
        {
            Vector3d c = Vector3d.Zero, v = Vector3d.Zero, o = vCenter.Origin;
            double   r = Radius;

            List <int> order = dj.GetOrder();
            int        N     = order.Count;

            for (int k = 0; k < N; ++k)
            {
                int vid = order[k];
                v = Mesh.GetVertex(vid);
                double d = v.Distance(ref o);
                double t = MathUtil.Clamp(d / r, 0.0, 1.0);
                t = 1 - t * t;
                t = t * t * t;
                //t = MathUtil.WyvillFalloff01(t);


                v = map[vid];
                c = Vector3d.Zero; int n = 0;
                foreach (int nbrid in Mesh.VtxVerticesItr(vid))
                {
                    c += map[nbrid];
                    n++;
                }
                c /= n;

                map[vid] = Vector3d.Lerp(ref v, ref c, SmoothPower * t);
            }
        }
        protected override void ApplyCurrentStamp(Frame3f vCenter, int tid, DijkstraGraphDistance dj,
                                                  DVector <Vector3d> Displacements, HashSet <int> ModifiedV)
        {
            List <int> order = dj.GetOrder();
            int        N     = order.Count;

            for (int k = 0; k < N; ++k)
            {
                int      vid = order[k];
                Vector3d v   = Mesh.GetVertex(vid);

                double d = v.Distance(vCenter.Origin);
                double t = MathUtil.Clamp(d / Radius, 0.0, 1.0);
                t = MathUtil.WyvillFalloff01(t);

                Vector3d c = Vector3d.Zero;
                Mesh.VtxOneRingCentroid(vid, ref c);

                t *= Power;
                Vector3d s = Vector3d.Lerp(ref v, ref c, t);

                Displacements[vid] = s;
                ModifiedV.Add(vid);
            }
        }
        void compute_inner_wall()
        {
            if (DebugStep <= 0)
            {
                SocketMesh = new DMesh3(TrimmedMesh);
                return;
            }

            InnerMesh = new DMesh3(TrimmedMesh);

            // compute flare band
            Func <int, double> flareOffsetF = (vid) => { return(0); };

            if (flare_offset > 0 && flare_band_width > 0)
            {
                MeshBoundaryLoops loops = new MeshBoundaryLoops(InnerMesh);
                if (loops.Count != 1)
                {
                    goto done_inner_wall;
                }

                DijkstraGraphDistance dist = DijkstraGraphDistance.MeshVertices(InnerMesh);
                dist.TrackOrder = true;
                foreach (int vid in loops[0].Vertices)
                {
                    dist.AddSeed(vid, 0);
                }
                dist.ComputeToMaxDistance(1.25f * (float)flare_band_width);

                flareOffsetF = (viD) => {
                    float d = dist.GetDistance(viD);
                    if (d < flare_band_width)
                    {
                        double t = d / flare_band_width;
                        t = 1 - t * t;
                        t = t * t * t;
                        return(t * flare_offset);
                    }
                    return(0);
                };
            }


            gParallel.ForEach(InnerMesh.VertexIndices(), (vid) => {
                Vector3d v    = InnerMesh.GetVertex(vid);
                Vector3d n    = InnerMesh.GetVertexNormal(vid);
                double offset = inner_offset + flareOffsetF(vid);
                InnerMesh.SetVertex(vid, v + offset * n);
            });


done_inner_wall:
            MeshNormals.QuickCompute(InnerMesh);
        }
        protected override void ApplyCurrentStamp(Frame3f vCenter, int tid, DijkstraGraphDistance dj, VectorDisplacement map)
        {
            List <int> order = dj.GetOrder();
            int        N     = order.Count;

            for (int k = 0; k < N; ++k)
            {
                int    vid = order[k];
                double d   = (Mesh.GetVertex(vid) - vCenter.Origin).Length;
                double t   = MathUtil.Clamp(d / Radius, 0.0, 1.0);
                t        = Power * MathUtil.WyvillFalloff01(t);
                map[vid] = Vector3d.Lerp(map[vid], Vector3d.Zero, t);
            }
        }
Exemple #9
0
        public static void profile_dijkstra_2b_reuse(int N = 500)
        {
            DMesh3 mesh = TestUtil.LoadTestInputMesh("sphere_50k_verts.obj");

            Func <int, int, float> VertexDistanceF = (v1, v2) => {
                return((float)mesh.GetVertex(v1).Distance(mesh.GetVertex(v2)));
            };

            LocalProfiler profiler = new LocalProfiler();

            DijkstraGraphDistance dist_sparse = new DijkstraGraphDistance(mesh.MaxVertexID, true,
                                                                          mesh.IsVertex, VertexDistanceF, mesh.VtxVerticesItr);

            for (int k = 0; k < N; ++k)
            {
                profiler.Start("dijkstra_sparse");
                dist_sparse.Reset();
                dist_sparse.AddSeed(0, 0);
                dist_sparse.Compute();

                profiler.StopAndAccumulate("dijkstra_sparse");
            }

            GC.Collect();

            DijkstraGraphDistance dist_dense = new DijkstraGraphDistance(mesh.MaxVertexID, false,
                                                                         mesh.IsVertex, VertexDistanceF, mesh.VtxVerticesItr);

            for (int k = 0; k < N; ++k)
            {
                profiler.Start("dijkstra_dense");
                dist_dense.Reset();
                dist_dense.AddSeed(0, 0);
                dist_dense.Compute();
                profiler.StopAndAccumulate("dijkstra_dense");
            }


            profiler.DivideAllAccumulated(N);
            System.Console.WriteLine(profiler.AllAccumulatedTimes());
        }
Exemple #10
0
        public void InitializeGeodesicDistance(Vector3d source, int source_tid)
        {
            DijkstraGraphDistance dist = new DijkstraGraphDistance(Mesh.MaxTriangleID, false,
                                                                   (tid) => { return(Mesh.IsTriangle(tid)); },
                                                                   (a, b) => { return((float)Mesh.GetTriCentroid(a).Distance(Mesh.GetTriCentroid(b))); },
                                                                   Mesh.TriTrianglesItr);

            dist.AddSeed(source_tid, (float)Mesh.GetTriCentroid(source_tid).Distance(source));

            //Index3i tri = Mesh.GetTriangle(source_tid);
            //for (int j = 0; j < 3; ++j) {
            //    dist.AddSeed(tri[j], (float)Mesh.GetVertex(tri[j]).Distance(source));
            //}

            dist.TrackOrder = true;
            dist.Compute();

            List <int> order = dist.GetOrder();

            tri_ordering = new OrderedTri[order.Count];
            for (int k = 0; k < order.Count; ++k)
            {
                tri_ordering[k].tid    = order[k];
                tri_ordering[k].scalar = dist.GetDistance(order[k]);
            }

            tid_to_order_idx = new int[Mesh.MaxTriangleID];
            for (int k = 0; k < order.Count; ++k)
            {
                tid_to_order_idx[order[k]] = k;
            }


            // rebuild chunks data structures
            update_ordered_chunks();
        }
        public override void Apply(Frame3f vNextPos, int tid)
        {
            if (Mesh.MaxVertexID > Displacements.Length)
            {
                Displacements.resize(Mesh.MaxVertexID);
            }
            ModifiedV.Clear();

            DijkstraGraphDistance dj = Dijkstra;

            dj.Reset();

            Index3i  ti = Mesh.GetTriangle(tid);
            Vector3d c  = Mesh.GetTriCentroid(tid);

            dj.AddSeed(ti.a, (float)Mesh.GetVertex(ti.a).Distance(c));
            dj.AddSeed(ti.b, (float)Mesh.GetVertex(ti.b).Distance(c));
            dj.AddSeed(ti.c, (float)Mesh.GetVertex(ti.c).Distance(c));
            double compute_Dist = MathUtil.SqrtTwo * Radius;

            dj.ComputeToMaxDistance((float)compute_Dist);

            ApplyCurrentStamp(vNextPos, tid, dj, Displacements, ModifiedV);
        }
        public virtual void Update()
        {
            if (MeshSource == null)
            {
                throw new Exception("PlaneBandExpansionOp: must set valid MeshSource to compute!");
            }

            IMesh imesh = MeshSource.GetIMesh();

            if (imesh.HasVertexNormals == false)
            {
                throw new Exception("PlaneBandExpansionOp: input mesh does not have surface normals...");
            }
            if (imesh is DMesh3 == false)
            {
                throw new Exception("RegionOffsetOp: in current implementation, input mesh must be a DMesh3. Ugh.");
            }
            DMesh3 mesh = imesh as DMesh3;

            IList <int> faces = IndexSource.GetIndices();

            // [RMS] this is all f'n ugly!

            MeshVertexSelection selection = new MeshVertexSelection(mesh);

            selection.SelectTriangleVertices(faces);

            // ugly
            List <Vector2d> seeds = new List <Vector2d>();

            foreach (int vid in selection)
            {
                foreach (int nbrvid in mesh.VtxVerticesItr(vid))
                {
                    if (selection.IsSelected(nbrvid) == false)
                    {
                        seeds.Add(new Vector2d(vid, 0));
                        break;
                    }
                }
            }
            Func <int, int, float> distanceF = (a, b) => { return((float)mesh.GetVertex(a).Distance(mesh.GetVertex(b))); };
            Func <int, bool>       nodeF     = (vid) => { return(selection.IsSelected(vid)); };
            DijkstraGraphDistance  dijkstra  = new DijkstraGraphDistance(mesh.MaxVertexID, true, nodeF, distanceF, mesh.VtxVerticesItr, seeds);

            dijkstra.Compute();
            float maxDist = dijkstra.MaxDistance;


            Displacement.Clear();
            Displacement.Resize(mesh.MaxVertexID);


            // todo: can do this in parallel...
            foreach (int vid in selection)
            {
                //Vector3d v = mesh.GetVertex(vid);

                // [TODO]...
                double dist    = maxDist - dijkstra.GetDistance(vid);
                double falloff = MathUtil.WyvillFalloff(dist, maxDist * 0.0, maxDist);

                Vector3d n = mesh.GetVertexNormal(vid);
                n = n - n.Dot(normal) * normal;
                n.Normalize();

                Displacement[vid] = falloff * offset_distance * n;
            }

            // smooth it?

            result_valid = true;
        }
        void generate_graph(DenseGrid3f supportGrid, DenseGridTrilinearImplicit distanceField)
        {
            int      ni = supportGrid.ni, nj = supportGrid.nj, nk = supportGrid.nk;
            float    dx     = (float)CellSize;
            Vector3f origin = this.GridOrigin;

            // parameters for initializing cost grid
            float MODEL_SPACE = 0.01f;      // needs small positive so that points on triangles count as inside (eg on ground plane)
            //float MODEL_SPACE = 2.0f*(float)CellSize;
            float CRAZY_DISTANCE   = 99999.0f;
            bool  UNIFORM_DISTANCE = true;
            float MAX_DIST         = 10 * (float)CellSize;


            // parameters for sorting seeds
            Vector3i center_idx = new Vector3i(ni / 2, 0, nk / 2);      // middle
            //Vector3i center_idx = new Vector3i(0, 0, 0);              // corner
            bool reverse_per_layer = true;


            DenseGrid3f costGrid = new DenseGrid3f(supportGrid);

            foreach (Vector3i ijk in costGrid.Indices())
            {
                Vector3d cell_center = new Vector3f(ijk.x * dx, ijk.y * dx, ijk.z * dx) + origin;
                float    f           = (float)distanceField.Value(ref cell_center);
                if (f <= MODEL_SPACE)
                {
                    f = CRAZY_DISTANCE;
                }
                else if (UNIFORM_DISTANCE)
                {
                    f = 1.0f;
                }
                else if (f > MAX_DIST)
                {
                    f = MAX_DIST;
                }
                costGrid[ijk] = f;
            }

            // Find seeds on each layer, sort, and add to accumulated bottom-up seeds list.
            // This sorting has an *enormous* effect on the support generation.

            List <Vector3i> seeds       = new List <Vector3i>();
            List <Vector3i> layer_seeds = new List <Vector3i>();

            for (int j = 0; j < nj; ++j)
            {
                layer_seeds.Clear();
                for (int k = 0; k < nk; ++k)
                {
                    for (int i = 0; i < ni; ++i)
                    {
                        if (supportGrid[i, j, k] == SUPPORT_TIP_BASE)
                        {
                            layer_seeds.Add(new Vector3i(i, j, k));
                        }
                    }
                }

                layer_seeds.Sort((a, b) => {
                    Vector3i pa = a; pa.y = 0;
                    Vector3i pb = b; pb.y = 0;
                    int sa      = (pa - center_idx).LengthSquared, sb = (pb - center_idx).LengthSquared;
                    return(sa.CompareTo(sb));
                });

                // reversing sort order is intresting?
                if (reverse_per_layer)
                {
                    layer_seeds.Reverse();
                }

                seeds.AddRange(layer_seeds);
            }
            HashSet <Vector3i> seed_indices = new HashSet <Vector3i>(seeds);

            // gives very different results...
            if (ProcessBottomUp == false)
            {
                seeds.Reverse();
            }

            // for linear index a, is this a node we allow in graph? (ie graph bounds)
            Func <int, bool> node_filter_f = (a) => {
                Vector3i ai = costGrid.to_index(a);
                // why not y check??
                return(ai.x > 0 && ai.z > 0 && ai.x != ni - 1 && ai.y != nj - 1 && ai.z != nk - 1);
            };

            // distance from linear index a to linear index b
            // this defines the cost field we want to find shortest path through
            Func <int, int, float> node_dist_f = (a, b) => {
                Vector3i ai = costGrid.to_index(a), bi = costGrid.to_index(b);
                if (bi.y >= ai.y)               // b.y should always be a.y-1
                {
                    return(float.MaxValue);
                }
                float sg = supportGrid[bi];

                // don't connect to tips
                //if (sg == SUPPORT_TIP_BASE || sg == SUPPORT_TIP_TOP)
                //    return float.MaxValue;
                if (sg == SUPPORT_TIP_TOP)
                {
                    return(float.MaxValue);
                }

                if (sg < 0)
                {
                    return(-999999);    // if b is already used, we will terminate there, so this is a good choice
                }
                // otherwise cost is sqr-grid-distance + costGrid value  (which is basically distance to surface)
                float c = costGrid[b];
                float f = (float)(Math.Sqrt((bi - ai).LengthSquared) * CellSize);
                //float f = 0;
                return(c + f);
            };

            // which linear-index nbrs to consider for linear index a
            Func <int, IEnumerable <int> > neighbour_f = (a) => {
                Vector3i ai = costGrid.to_index(a);
                return(down_neighbours(ai, costGrid));
            };

            // when do we terminate
            Func <int, bool> terminate_f = (a) => {
                Vector3i ai = costGrid.to_index(a);
                // terminate if we hit existing support path
                if (seed_indices.Contains(ai) == false && supportGrid[ai] < 0)
                {
                    return(true);
                }
                // terminate if we hit ground plane
                if (ai.y == 0)
                {
                    return(true);
                }
                return(false);
            };

            DijkstraGraphDistance dijkstra = new DijkstraGraphDistance(ni * nj * nk, false,
                                                                       node_filter_f, node_dist_f, neighbour_f);

            dijkstra.TrackOrder = true;

            List <int> path = new List <int>();

            Graph = new DGraph3();
            Dictionary <Vector3i, int> CellToGraph = new Dictionary <Vector3i, int>();

            TipVertices     = new HashSet <int>();
            TipBaseVertices = new HashSet <int>();
            GroundVertices  = new HashSet <int>();

            // seeds are tip-base points
            for (int k = 0; k < seeds.Count; ++k)
            {
                // add seed point (which is a tip-base vertex) as seed for dijkstra prop
                int seed = costGrid.to_linear(seeds[k]);
                dijkstra.Reset();
                dijkstra.AddSeed(seed, 0);

                // compute to termination (ground, existing node, etc)
                int base_node = dijkstra.ComputeToNode(terminate_f);
                if (base_node < 0)
                {
                    base_node = dijkstra.GetOrder().Last();
                }

                // extract the path
                path.Clear();
                dijkstra.GetPathToSeed(base_node, path);
                int N = path.Count;

                // first point on path is termination point.
                // create vertex for it if we have not yet
                Vector3i basept_idx = supportGrid.to_index(path[0]);
                int      basept_vid;
                if (CellToGraph.TryGetValue(basept_idx, out basept_vid) == false)
                {
                    Vector3d curv = get_cell_center(basept_idx);
                    if (basept_idx.y == 0)
                    {
                        curv.y = 0;
                    }
                    basept_vid = Graph.AppendVertex(curv);
                    if (basept_idx.y == 0)
                    {
                        GroundVertices.Add(basept_vid);
                    }
                    CellToGraph[basept_idx] = basept_vid;
                }

                int cur_vid = basept_vid;

                // now walk up path and create vertices as necessary
                for (int i = 0; i < N; ++i)
                {
                    int idx = path[i];
                    if (supportGrid[idx] >= 0)
                    {
                        supportGrid[idx] = SUPPORT_GRID_USED;
                    }
                    if (i > 0)
                    {
                        Vector3i next_idx = supportGrid.to_index(path[i]);
                        int      next_vid;
                        if (CellToGraph.TryGetValue(next_idx, out next_vid) == false)
                        {
                            Vector3d nextv = get_cell_center(next_idx);
                            next_vid = Graph.AppendVertex(nextv);
                            CellToGraph[next_idx] = next_vid;
                        }
                        Graph.AppendEdge(cur_vid, next_vid);
                        cur_vid = next_vid;
                    }
                }

                // seed was tip-base so we should always get back there. Then we
                // explicitly add tip-top and edge to it.
                if (supportGrid[path[N - 1]] == SUPPORT_TIP_BASE)
                {
                    Vector3i vec_idx = supportGrid.to_index(path[N - 1]);
                    TipBaseVertices.Add(CellToGraph[vec_idx]);

                    Vector3i tip_idx = vec_idx + Vector3i.AxisY;
                    int      tip_vid;
                    if (CellToGraph.TryGetValue(tip_idx, out tip_vid) == false)
                    {
                        Vector3d tipv = get_cell_center(tip_idx);
                        tip_vid = Graph.AppendVertex(tipv);
                        CellToGraph[tip_idx] = tip_vid;
                        Graph.AppendEdge(cur_vid, tip_vid);
                        TipVertices.Add(tip_vid);
                    }
                }
            }



            /*
             * Snap tips to surface
             */

            gParallel.ForEach(TipVertices, (tip_vid) => {
                bool snapped = false;
                Vector3d v   = Graph.GetVertex(tip_vid);
                Frame3f hitF;
                // try shooting ray straight up. if that hits, and point is close, we use it
                if (MeshQueries.RayHitPointFrame(Mesh, MeshSpatial, new Ray3d(v, Vector3d.AxisY), out hitF))
                {
                    if (v.Distance(hitF.Origin) < 2 * CellSize)
                    {
                        v       = hitF.Origin;
                        snapped = true;
                    }
                }

                // if that failed, try straight down
                if (!snapped)
                {
                    if (MeshQueries.RayHitPointFrame(Mesh, MeshSpatial, new Ray3d(v, -Vector3d.AxisY), out hitF))
                    {
                        if (v.Distance(hitF.Origin) < CellSize)
                        {
                            v       = hitF.Origin;
                            snapped = true;
                        }
                    }
                }

                // if it missed, or hit pt was too far, find nearest point and try that
                if (!snapped)
                {
                    hitF = MeshQueries.NearestPointFrame(Mesh, MeshSpatial, v);
                    if (v.Distance(hitF.Origin) < 2 * CellSize)
                    {
                        v       = hitF.Origin;
                        snapped = true;
                    }
                    // can this ever fail? tips should always be within 2 cells...
                }
                if (snapped)
                {
                    Graph.SetVertex(tip_vid, v);
                }
            });
        }
Exemple #14
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);
        }
 /// <summary>
 /// Subclass implements this
 /// </summary>
 protected abstract void ApplyCurrentStamp(Frame3f vCenter, int tid, DijkstraGraphDistance dj, VectorDisplacement map);
 protected override void mesh_changed()
 {
     dist = null;
 }
 /// <summary>
 /// Subclass implements this
 /// </summary>
 protected abstract void ApplyCurrentStamp(Frame3f vCenter, int tid, DijkstraGraphDistance dj,
                                           DVector <Vector3d> Displacements, HashSet <int> ModifiedV);