예제 #1
0
 public GraphTubeMesher(GraphSupportGenerator support_gen)
 {
     Graph               = support_gen.Graph;
     TipVertices         = support_gen.TipVertices;
     GroundVertices      = support_gen.GroundVertices;
     SamplerCellSizeHint = support_gen.CellSize;
 }
예제 #2
0
        void update()
        {
            MeshIsoCurves iso = new MeshIsoCurves(SO.Mesh, (v) => {
                return((v - frameL.Origin).Dot(frameL.Z));
            });

            iso.Compute();
            graph       = iso.Graph;
            localCurves = DGraph3Util.ExtractCurves(graph);

            // ugh need xform seq for to/from world...
            if (OutputSpace == CoordSpace.WorldCoords)
            {
                foreach (DCurve3 c in localCurves.Loops)
                {
                    for (int i = 0; i < c.VertexCount; ++i)
                    {
                        c[i] = SceneTransforms.TransformTo((Vector3f)c[i], SO, CoordSpace.ObjectCoords, OutputSpace);
                    }
                }
                foreach (DCurve3 c in localCurves.Paths)
                {
                    for (int i = 0; i < c.VertexCount; ++i)
                    {
                        c[i] = SceneTransforms.TransformTo((Vector3f)c[i], SO, CoordSpace.ObjectCoords, OutputSpace);
                    }
                }
            }
            else if (OutputSpace == CoordSpace.SceneCoords)
            {
                TransformSequence xform = SceneTransforms.ObjectToSceneXForm(SO);
                foreach (DCurve3 c in localCurves.Loops)
                {
                    for (int i = 0; i < c.VertexCount; ++i)
                    {
                        c[i] = xform.TransformP(c[i]);
                    }
                }
                foreach (DCurve3 c in localCurves.Paths)
                {
                    for (int i = 0; i < c.VertexCount; ++i)
                    {
                        c[i] = xform.TransformP(c[i]);
                    }
                }
            }
        }
예제 #3
0
        private static bool compute_plane_curves(DMesh3 mesh, DMeshAABBTree3 spatial,
                                                 double z, bool is_solid,
                                                 out Polygon2d[] loops, out PolyLine2d[] curves)
        {
            Func <Vector3d, double> planeF = (v) =>
            {
                return(v.z - z);
            };

            // find list of triangles that intersect this z-value
            PlaneIntersectionTraversal planeIntr = new PlaneIntersectionTraversal(mesh, z);

            spatial.DoTraversal(planeIntr);
            List <int> triangles = planeIntr.triangles;

            // compute intersection iso-curves, which produces a 3D graph of undirected edges
            MeshIsoCurves iso = new MeshIsoCurves(mesh, planeF)
            {
                WantGraphEdgeInfo = true
            };

            iso.Compute(triangles);
            DGraph3 graph = iso.Graph;

            if (graph.EdgeCount == 0)
            {
                loops  = new Polygon2d[0];
                curves = new PolyLine2d[0];
                return(false);
            }

            // if this is a closed solid, any open spurs in the graph are errors
            if (is_solid)
            {
                DGraph3Util.ErodeOpenSpurs(graph);
            }

            // [RMS] debug visualization
            //DGraph2 graph2 = new DGraph2();
            //Dictionary<int, int> mapV = new Dictionary<int, int>();
            //foreach (int vid in graph.VertexIndices())
            //    mapV[vid] = graph2.AppendVertex(graph.GetVertex(vid).xy);
            //foreach (int eid in graph.EdgeIndices())
            //    graph2.AppendEdge(mapV[graph.GetEdge(eid).a], mapV[graph.GetEdge(eid).b]);
            //SVGWriter svg = new SVGWriter();
            //svg.AddGraph(graph2, SVGWriter.Style.Outline("black", 0.05f));
            //foreach (int vid in graph2.VertexIndices()) {
            //    if (graph2.IsJunctionVertex(vid))
            //        svg.AddCircle(new Circle2d(graph2.GetVertex(vid), 0.25f), SVGWriter.Style.Outline("red", 0.1f));
            //    else if (graph2.IsBoundaryVertex(vid))
            //        svg.AddCircle(new Circle2d(graph2.GetVertex(vid), 0.25f), SVGWriter.Style.Outline("blue", 0.1f));
            //}
            //svg.Write(string.Format("c:\\meshes\\EXPORT_SLICE_{0}.svg", z));

            // extract loops and open curves from graph
            DGraph3Util.Curves c = DGraph3Util.ExtractCurves(graph, false, iso.ShouldReverseGraphEdge);
            loops = new Polygon2d[c.Loops.Count];
            for (int li = 0; li < loops.Length; ++li)
            {
                DCurve3 loop = c.Loops[li];
                loops[li] = new Polygon2d();
                foreach (Vector3d v in loop.Vertices)
                {
                    loops[li].AppendVertex(v.xy);
                }
            }

            curves = new PolyLine2d[c.Paths.Count];
            for (int pi = 0; pi < curves.Length; ++pi)
            {
                DCurve3 span = c.Paths[pi];
                curves[pi] = new PolyLine2d();
                foreach (Vector3d v in span.Vertices)
                {
                    curves[pi].AppendVertex(v.xy);
                }
            }

            return(true);
        }
예제 #4
0
        void constrained_smooth(DGraph3 graph, double surfDist, double dotThresh, double alpha, int rounds)
        {
            int NV = graph.MaxVertexID;

            Vector3d[] pos = new Vector3d[NV];

            for (int ri = 0; ri < rounds; ++ri)
            {
                gParallel.ForEach(graph.VertexIndices(), (vid) => {
                    Vector3d v = graph.GetVertex(vid);

                    if (GroundVertices.Contains(vid) || TipVertices.Contains(vid))
                    {
                        pos[vid] = v;
                        return;
                    }

                    // for tip base vertices, we could allow them to move down and away within angle cone...
                    if (TipBaseVertices.Contains(vid))
                    {
                        pos[vid] = v;
                        return;
                    }


                    // compute smoothed position of vtx
                    Vector3d centroid = Vector3d.Zero; int nbr_count = 0;
                    foreach (int nbr_vid in graph.VtxVerticesItr(vid))
                    {
                        centroid += graph.GetVertex(nbr_vid);
                        nbr_count++;
                    }
                    if (nbr_count == 1)
                    {
                        pos[vid] = v;
                        return;
                    }
                    centroid     /= nbr_count;
                    Vector3d vnew = (1 - alpha) * v + (alpha) * centroid;

                    // make sure we don't violate angle constraint to any nbrs
                    int attempt = 0;
                    try_again:
                    foreach (int nbr_vid in graph.VtxVerticesItr(vid))
                    {
                        Vector3d dv = graph.GetVertex(nbr_vid) - vnew;
                        dv.Normalize();
                        double dot = dv.Dot(Vector3d.AxisY);
                        if (Math.Abs(dot) < dotThresh)
                        {
                            if (attempt++ < 3)
                            {
                                vnew = Vector3d.Lerp(v, vnew, 0.66);
                                goto try_again;
                            }
                            else
                            {
                                pos[vid] = v;
                                return;
                            }
                        }
                    }

                    // offset from nearest point on surface
                    Frame3f fNearest  = MeshQueries.NearestPointFrame(Mesh, MeshSpatial, vnew, true);
                    Vector3d vNearest = fNearest.Origin;
                    double dist       = vnew.Distance(vNearest);
                    bool inside       = MeshSpatial.IsInside(vnew);

                    if (inside || dist < surfDist)
                    {
                        Vector3d normal = fNearest.Z;
                        // don't push down?
                        if (normal.Dot(Vector3d.AxisY) < 0)
                        {
                            normal.y = 0; normal.Normalize();
                        }
                        vnew = fNearest.Origin + surfDist * normal;
                    }


                    pos[vid] = vnew;
                });

                foreach (int vid in graph.VertexIndices())
                {
                    graph.SetVertex(vid, pos[vid]);
                }
            }
        }
예제 #5
0
        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);
                }
            });
        }
예제 #6
0
        void extract_topology()
        {
            var graph = new DGraph3();

            // add vertices to graph, and store mappings
            int[] mapV     = new int[Mesh.MaxVertexID];
            int[] mapVFrom = new int[AllVertices.Count];
            foreach (int vid in AllVertices)
            {
                int new_vid = graph.AppendVertex(Mesh.GetVertex(vid));
                mapV[vid]         = new_vid;
                mapVFrom[new_vid] = vid;
            }

            // add edges to graph. graph-to-mesh eid mapping is stored via graph edge-group-id
            int[] mapE = new int[Mesh.MaxEdgeID];
            foreach (int eid in AllEdges)
            {
                Index2i ev      = Mesh.GetEdgeV(eid);
                int     new_a   = mapV[ev.a];
                int     new_b   = mapV[ev.b];
                int     new_eid = graph.AppendEdge(new_a, new_b, eid);
                mapE[eid] = new_eid;
            }

            // extract the graph topology
            DGraph3Util.Curves curves = DGraph3Util.ExtractCurves(graph, true);

            // reconstruct mesh spans / curves / junctions from graph topology

            int NP = curves.PathEdges.Count;

            Spans = new EdgeSpan[NP];
            for (int pi = 0; pi < NP; ++pi)
            {
                List <int> pathE = curves.PathEdges[pi];
                for (int k = 0; k < pathE.Count; ++k)
                {
                    pathE[k] = graph.GetEdgeGroup(pathE[k]);
                }
                Spans[pi] = EdgeSpan.FromEdges(Mesh, pathE);
            }

            int NL = curves.LoopEdges.Count;

            Loops = new EdgeLoop[NL];
            for (int li = 0; li < NL; ++li)
            {
                List <int> loopE = curves.LoopEdges[li];
                for (int k = 0; k < loopE.Count; ++k)
                {
                    loopE[k] = graph.GetEdgeGroup(loopE[k]);
                }
                Loops[li] = EdgeLoop.FromEdges(Mesh, loopE);
            }

            JunctionVertices = new HashSet <int>();
            foreach (int gvid in curves.JunctionV)
            {
                JunctionVertices.Add(mapVFrom[gvid]);
            }
        }
예제 #7
0
        public virtual void Update()
        {
            base.begin_update();
            int start_timestamp = this.CurrentInputTimestamp;

            if (MeshSource == null)
            {
                throw new Exception("GenerateGraphSupportsOp: must set valid MeshSource to compute!");
            }

            try {
                ResultMesh = null;

                DMesh3 mesh = MeshSource.GetDMeshUnsafe();

                GraphSupportGenerator supportgen = new GraphSupportGenerator(mesh, get_cached_spatial(), GridCellSize);
                supportgen.OverhangAngleDeg           = this.overhang_angle;
                supportgen.ForceMinY                  = (float)this.min_y;
                supportgen.ProcessBottomUp            = this.bottom_up;
                supportgen.OverhangAngleOptimizeDeg   = this.support_min_angle;
                supportgen.OptimizationRounds         = this.optimize_rounds;
                supportgen.GraphSurfaceDistanceOffset = this.post_diam / 2 + surface_offset_distance;

                supportgen.Progress = new ProgressCancel(is_invalidated);
                supportgen.Generate();
                DGraph3 graph = supportgen.Graph;

                if (is_invalidated())
                {
                    goto skip_to_end;
                }

                GraphTubeMesher mesher = new GraphTubeMesher(supportgen);
                mesher.TipRadius           = this.tip_diam / 2;
                mesher.PostRadius          = this.post_diam / 2;
                mesher.GroundRadius        = this.base_diam / 2;
                mesher.SamplerCellSizeHint = supportgen.CellSize / 2;
                mesher.Progress            = new ProgressCancel(is_invalidated);
                mesher.Generate();

                if (is_invalidated())
                {
                    goto skip_to_end;
                }

                ResultMesh = mesher.ResultMesh;
                Reducer reducer = new Reducer(ResultMesh);
                reducer.Progress = new ProgressCancel(is_invalidated);
                reducer.ReduceToEdgeLength(mesher.ActualCellSize / 2);

skip_to_end:
                if (is_invalidated())
                {
                    ResultMesh = null;
                }
                base.complete_update();
            } catch (Exception e) {
                PostOnOperatorException(e);
                ResultMesh = base.make_failure_output(MeshSource.GetDMeshUnsafe());
                base.complete_update();
            }
        }
예제 #8
0
 public GraphTubeMesher(DGraph3 graph)
 {
     Graph = graph;
 }