public GraphTubeMesher(GraphSupportGenerator support_gen) { Graph = support_gen.Graph; TipVertices = support_gen.TipVertices; GroundVertices = support_gen.GroundVertices; SamplerCellSizeHint = support_gen.CellSize; }
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]); } } } }
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); }
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]); } } }
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); } }); }
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]); } }
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(); } }
public GraphTubeMesher(DGraph3 graph) { Graph = graph; }