InteractiveRemesher make_remesher(DMesh3 mesh) { var m = new InteractiveRemesher(mesh); m.PreventNormalFlips = true; double mine, maxe, avge; MeshQueries.EdgeLengthStats(mesh, out mine, out avge, out maxe); m.SetTargetEdgeLength(avge * EdgeLengthMultiplier); m.SmoothSpeedT = SmoothSpeed; if (Reproject) { m.SetProjectionTarget(MeshProjectionTarget.Auto(mesh)); } if (RemeshBoundary) { MeshBoundaryLoops loops = new MeshBoundaryLoops(mesh); int k = 1; foreach (var loop in loops) { MeshConstraintUtil.ConstrainVtxLoopTo(m, loop.Vertices, new DCurveProjectionTarget(loop.ToCurve()), k++); } } else if (PreserveBoundary) { MeshConstraintUtil.FixAllBoundaryEdges(m); } return(m); }
DMesh3 GenerateRemesh(DMesh3 mesh) { DMesh3 remeshed = new DMesh3(mesh); DMeshAABBTree3 project = new DMeshAABBTree3(mesh); project.Build(); MeshProjectionTarget Target = new MeshProjectionTarget(project.Mesh, project); double minlen, maxlen, avglen; MeshQueries.EdgeLengthStats(mesh, out minlen, out maxlen, out avglen); double edge_len = (TargetEdgeLength == 0) ? Loop.AverageEdgeLength : avglen; Remesher r = new Remesher(remeshed); r.SetTargetEdgeLength(edge_len); r.SetProjectionTarget(Target); MeshConstraintUtil.FixAllBoundaryEdges(r); for (int k = 0; k < 20; ++k) { r.BasicRemeshPass(); } return(remeshed); }
DMesh3 BuildPlanarMesh(bool bPreservePolygon) { DMesh3 planarMesh = new DMesh3(); Vector2d center = CurveUtils2.CentroidVtx(Loop.Vertices); int center_id = planarMesh.AppendVertex(new Vector3d(center.x, center.y, 0)); int prev_id = -1; int first_id = -1; foreach (Vector2d v in Loop.Vertices) { int next_id = planarMesh.AppendVertex(new Vector3d(v.x, v.y, Thickness)); if (prev_id > 0) { planarMesh.AppendTriangle(center_id, prev_id, next_id); prev_id = next_id; } else { first_id = next_id; prev_id = next_id; } } planarMesh.AppendTriangle(center_id, prev_id, first_id); if (ReverseOrientation) { planarMesh.ReverseOrientation(); } Debug.Assert(planarMesh.CheckValidity()); double edge_len = (TargetEdgeLength == 0) ? Loop.AverageEdgeLength : TargetEdgeLength; Remesher r = new Remesher(planarMesh); r.SetTargetEdgeLength(edge_len); r.SmoothSpeedT = 1.0f; if (bPreservePolygon) { MeshConstraintUtil.FixAllBoundaryEdges(r); } else { MeshConstraintUtil.PreserveBoundaryLoops(r); } for (int k = 0; k < 20; ++k) { r.BasicRemeshPass(); } return(planarMesh); }
protected void generate(float fDiameter, float fHeight, float fWallThickness, float fBaseThickness) { base.reset_holes(); CappedCylinderGenerator outer_cylgen = new CappedCylinderGenerator() { BaseRadius = fDiameter / 2, TopRadius = fDiameter / 2, Height = fHeight + 10, Slices = 60, Clockwise = true }; DMesh3 outer_mesh = outer_cylgen.Generate().MakeDMesh(); float fInnerDiam = fDiameter - 2 * fWallThickness; CappedCylinderGenerator inner_cylgen = new CappedCylinderGenerator() { BaseRadius = fInnerDiam / 2, TopRadius = fInnerDiam / 2, Height = fHeight + 10, Slices = 60, Clockwise = false }; DMesh3 inner_mesh = inner_cylgen.Generate().MakeDMesh(); MeshTransforms.Translate(inner_mesh, fBaseThickness * Vector3d.AxisY); DMesh3[] meshes = new DMesh3[2] { outer_mesh, inner_mesh }; foreach (DMesh3 mesh in meshes) { Remesher r = new Remesher(mesh); r.SetTargetEdgeLength(TargetEdgeLength); r.SmoothSpeedT = 0.5f; r.SetExternalConstraints(new MeshConstraints()); MeshConstraintUtil.FixAllGroupBoundaryEdges(r.Constraints, mesh, true); r.SetProjectionTarget(MeshProjectionTarget.Auto(mesh)); for (int k = 0; k < 10; ++k) { r.BasicRemeshPass(); } } Vector3d vCutPos = new Vector3d(0, fHeight, 0); Vector3d vCutNormal = Vector3d.AxisY; foreach (DMesh3 mesh in meshes) { MeshPlaneCut cut = new MeshPlaneCut(mesh, new Vector3d(0, fHeight, 0), Vector3d.AxisY); cut.Cut(); } base.set_output_meshes(inner_mesh, outer_mesh); }
void optimize_mesh(DMesh3 mesh) { Reducer reducer = new Reducer(mesh); MeshConstraints constraints = new MeshConstraints(); MeshConstraintUtil.FixAllBoundaryEdges(constraints, mesh); reducer.SetExternalConstraints(constraints); reducer.ReduceToTriangleCount(1); Vector3d a, b, c, d; a = b = c = d = Vector3d.Zero; bool done = false; while (!done) { done = true; for (int eid = 0; eid < mesh.MaxEdgeID; ++eid) { if (mesh.IsEdge(eid) == false) { continue; } Index4i evt = mesh.GetEdge(eid); if (evt.d == DMesh3.InvalidID) { continue; } a = mesh.GetVertex(evt.a); b = mesh.GetVertex(evt.b); Index2i ov = mesh.GetEdgeOpposingV(eid); c = mesh.GetVertex(ov.a); d = mesh.GetVertex(ov.b); if (c.DistanceSquared(d) > a.DistanceSquared(b)) { continue; } if (MeshUtil.CheckIfEdgeFlipCreatesFlip(mesh, eid)) { continue; } DMesh3.EdgeFlipInfo flipInfo; if (mesh.FlipEdge(eid, out flipInfo) == MeshResult.Ok) { done = false; } } } }
private DMesh3 Decimate(DMesh3 mesh, int targetCount) { Reducer r = new Reducer(mesh); if (_options.ReduceMeshPreserveEdges) { r.SetExternalConstraints(new MeshConstraints()); MeshConstraintUtil.FixAllBoundaryEdges(r.Constraints, mesh); } r.ReduceToTriangleCount(targetCount); return(mesh); }
protected override void SolveInstance(IGH_DataAccess DA) { DMesh3_goo dMsh_goo = null; double targetL = 0; int numI = 0; bool fixB = false; bool projBack = false; DA.GetData(0, ref dMsh_goo); DA.GetData(1, ref targetL); DA.GetData(2, ref numI); DA.GetData(3, ref fixB); DA.GetData(4, ref projBack); DMesh3 dMsh_copy = new DMesh3(dMsh_goo.Value); Remesher r = new Remesher(dMsh_copy); r.PreventNormalFlips = true; r.SetTargetEdgeLength(targetL); r.SmoothSpeedT = 0.5; if (fixB) { MeshConstraintUtil.FixAllBoundaryEdges(r); } if (projBack) { r.SetProjectionTarget(MeshProjectionTarget.Auto(dMsh_goo.Value)); } for (int k = 0; k < numI; ++k) { r.BasicRemeshPass(); } bool isValid = dMsh_copy.CheckValidity(); if (!isValid) { this.AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Mesh seems to have been corrupted during remeshing. Please check..."); } DA.SetData(0, dMsh_copy); }
public static DMesh3 ReduceTriangles(this DMesh3 dMesh, int triangleCount, bool computeNormals = true, bool fixAllBoundaryEdges = true) { Reducer reducer = new Reducer(dMesh); if (fixAllBoundaryEdges) { reducer.SetExternalConstraints(meshConstraints); MeshConstraintUtil.FixAllBoundaryEdges(reducer.Constraints, dMesh); } reducer.ReduceToTriangleCount(triangleCount); if (computeNormals) { MeshNormals.QuickCompute(dMesh); } return(dMesh); }
protected override void SolveInstance(IGH_DataAccess DA) { DMesh3_goo dMsh_goo = null; int numF = 0; bool fixB = false; bool projBack = false; DA.GetData(0, ref dMsh_goo); DA.GetData(1, ref numF); DA.GetData(2, ref fixB); DA.GetData(3, ref projBack); DMesh3 dMsh_copy = new DMesh3(dMsh_goo.Value); Reducer r = new Reducer(dMsh_copy); if (fixB) { r.SetExternalConstraints(new MeshConstraints()); MeshConstraintUtil.PreserveBoundaryLoops(r.Constraints, dMsh_copy); } if (projBack) { DMeshAABBTree3 tree = new DMeshAABBTree3(new DMesh3(dMsh_copy)); tree.Build(); MeshProjectionTarget target = new MeshProjectionTarget(tree.Mesh, tree); r.SetProjectionTarget(target); } r.ReduceToTriangleCount(numF); bool isValid = dMsh_copy.CheckValidity(); if (!isValid) { this.AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Mesh seems to have been corrupted during reduction. Please check..."); } DA.SetData(0, dMsh_copy); }
/// <summary> /// add topological edges/vertices as constraints for remeshing /// </summary> public void AddRemeshConstraints(MeshConstraints constraints) { validate_topology(); int set_index = 10; foreach (EdgeSpan span in Spans) { var target = new DCurveProjectionTarget(span.ToCurve()); MeshConstraintUtil.ConstrainVtxSpanTo(constraints, Mesh, span.Vertices, target, set_index++); } foreach (EdgeLoop loop in Loops) { var target = new DCurveProjectionTarget(loop.ToCurve()); MeshConstraintUtil.ConstrainVtxLoopTo(constraints, Mesh, loop.Vertices, target, set_index++); } VertexConstraint corners = VertexConstraint.Pinned; corners.FixedSetID = -1; foreach (int vid in JunctionVertices) { if (constraints.HasVertexConstraint(vid)) { VertexConstraint v = constraints.GetVertexConstraint(vid); v.Target = null; v.Fixed = true; v.FixedSetID = -1; constraints.SetOrUpdateVertexConstraint(vid, v); } else { constraints.SetOrUpdateVertexConstraint(vid, corners); } } }
protected virtual DMesh3 compute_sharp_edge_flow() { DMesh3 sourceMesh = MeshSource.GetDMeshUnsafe(); ISpatial inputSpatial = MeshSource.GetSpatial(); DMesh3 targetMesh = TargetSource.GetDMeshUnsafe(); ISpatial targetSpatial = TargetSource.GetSpatial(); DMesh3 meshIn = new DMesh3(sourceMesh); if (is_invalidated()) { return(null); } RemesherPro remesher = new RemesherPro(meshIn); remesher.SetTargetEdgeLength(TargetEdgeLength); remesher.PreventNormalFlips = this.PreventNormalFlips; remesher.EnableFlips = this.EnableFlips; remesher.EnableSplits = this.EnableSplits; remesher.EnableCollapses = this.EnableCollapses; remesher.EnableSmoothing = this.EnableSmoothing; remesher.SmoothSpeedT = this.SmoothingSpeed; TransformedMeshProjectionTarget target = new TransformedMeshProjectionTarget(targetMesh, targetSpatial) { SourceToTargetXForm = source_to_target, TargetToSourceXForm = target_to_source }; remesher.SetProjectionTarget(target); if (sourceMesh.CachedIsClosed == false) { if (remesher.Constraints == null) { remesher.SetExternalConstraints(new MeshConstraints()); } if (BoundaryMode == BoundaryModes.FreeBoundaries) { MeshConstraintUtil.PreserveBoundaryLoops(remesher.Constraints, meshIn); } else if (BoundaryMode == BoundaryModes.FixedBoundaries) { MeshConstraintUtil.FixAllBoundaryEdges(remesher.Constraints, meshIn); } else if (BoundaryMode == BoundaryModes.ConstrainedBoundaries) { MeshConstraintUtil.FixAllBoundaryEdges_AllowSplit(remesher.Constraints, meshIn, 0); } } if (is_invalidated()) { return(null); } remesher.Progress = new ProgressCancel(is_invalidated); remesher.SharpEdgeReprojectionRemesh(RemeshRounds, ProjectionRounds); if (is_invalidated()) { return(null); } return(meshIn); }
public static void quick_test_2() { DMesh3 target = TestUtil.LoadTestInputMesh("cylinder_orig.obj"); DMeshAABBTree3 targetSpatial = new DMeshAABBTree3(target, true); DMesh3 mesh = TestUtil.LoadTestInputMesh("cylinder_approx.obj"); DMeshAABBTree3 meshSpatial = new DMeshAABBTree3(mesh, true); double search_dist = 10.0; MeshTopology topo = new MeshTopology(target); topo.Compute(); RemesherPro r = new RemesherPro(mesh); r.SetTargetEdgeLength(2.0); r.SmoothSpeedT = 0.5; r.SetProjectionTarget(MeshProjectionTarget.Auto(target)); MeshConstraints cons = new MeshConstraints(); r.SetExternalConstraints(cons); int set_id = 1; foreach (var loop in topo.Loops) { DCurveProjectionTarget curveTarget = new DCurveProjectionTarget(loop.ToCurve(target)); set_id++; // pick a set of points we will find paths between. We will chain // up those paths and constrain them to target loops. // (this part is the hack!) List <int> target_verts = new List <int>(); List <int> mesh_verts = new List <int>(); for (int k = 0; k < loop.VertexCount; k += 5) { target_verts.Add(loop.Vertices[k]); Vector3d vCurve = target.GetVertex(loop.Vertices[k]); int mesh_vid = meshSpatial.FindNearestVertex(vCurve, search_dist); mesh_verts.Add(mesh_vid); } int NT = target_verts.Count; // find the paths to assemble the edge chain // [TODO] need to filter out junction vertices? or will they just handle themselves // because they can be collapsed away? List <int> vert_seq = new List <int>(); for (int k = 0; k < NT; k++) { EdgeSpan e = find_edge_path(mesh, mesh_verts[k], mesh_verts[(k + 1) % NT]); int n = e.Vertices.Length; for (int i = 0; i < n - 1; ++i) { vert_seq.Add(e.Vertices[i]); } } // now it's easy, just add the loop constraint EdgeLoop full_loop = EdgeLoop.FromVertices(mesh, vert_seq); MeshConstraintUtil.ConstrainVtxLoopTo(cons, mesh, full_loop.Vertices, curveTarget, set_id); } r.FastestRemesh(); TestUtil.WriteTestOutputMesh(mesh, "curves_test_out.obj"); }
public void Close_Flat() { double minlen, maxlen, avglen; MeshQueries.EdgeLengthStats(Mesh, out minlen, out maxlen, out avglen, 1000); double target_edge_len = (TargetEdgeLen <= 0) ? avglen : TargetEdgeLen; // massage around boundary loop cleanup_boundary(Mesh, InitialBorderLoop, avglen, 3); // find new border loop // [TODO] this just assumes there is only one!! MeshBoundaryLoops loops = new MeshBoundaryLoops(Mesh); EdgeLoop fill_loop = loops.Loops[0]; int extrude_group = (ExtrudeGroup == -1) ? Mesh.AllocateTriangleGroup() : ExtrudeGroup; int fill_group = (FillGroup == -1) ? Mesh.AllocateTriangleGroup() : FillGroup; // decide on projection plane //AxisAlignedBox3d loopbox = fill_loop.GetBounds(); //Vector3d topPt = loopbox.Center; //if ( bIsUpper ) { // topPt.y = loopbox.Max.y + 0.25 * dims.y; //} else { // topPt.y = loopbox.Min.y - 0.25 * dims.y; //} //Frame3f plane = new Frame3f((Vector3f)topPt); // extrude loop to this plane MeshExtrusion extrude = new MeshExtrusion(Mesh, fill_loop); extrude.PositionF = (v, n, i) => { return(FlatClosePlane.ProjectToPlane((Vector3F)v, 1)); }; extrude.Extrude(extrude_group); MeshValidation.IsBoundaryLoop(Mesh, extrude.NewLoop); Debug.Assert(Mesh.CheckValidity()); // smooth the extrude loop MeshLoopSmooth loop_smooth = new MeshLoopSmooth(Mesh, extrude.NewLoop); loop_smooth.ProjectF = (v, i) => { return(FlatClosePlane.ProjectToPlane((Vector3F)v, 1)); }; loop_smooth.Alpha = 0.5f; loop_smooth.Rounds = 100; loop_smooth.Smooth(); Debug.Assert(Mesh.CheckValidity()); // fill result SimpleHoleFiller filler = new SimpleHoleFiller(Mesh, extrude.NewLoop); filler.Fill(fill_group); Debug.Assert(Mesh.CheckValidity()); // make selection for remesh region MeshFaceSelection remesh_roi = new MeshFaceSelection(Mesh); remesh_roi.Select(extrude.NewTriangles); remesh_roi.Select(filler.NewTriangles); remesh_roi.ExpandToOneRingNeighbours(); remesh_roi.ExpandToOneRingNeighbours(); remesh_roi.LocalOptimize(true, true); int[] new_roi = remesh_roi.ToArray(); // get rid of extrude group FaceGroupUtil.SetGroupToGroup(Mesh, extrude_group, 0); /* clean up via remesh * - constrain loop we filled to itself */ RegionRemesher r = new RegionRemesher(Mesh, new_roi); DCurve3 top_curve = mesh.MeshUtil.ExtractLoopV(Mesh, extrude.NewLoop.Vertices); DCurveProjectionTarget curve_target = new DCurveProjectionTarget(top_curve); int[] top_loop = (int[])extrude.NewLoop.Vertices.Clone(); r.Region.MapVerticesToSubmesh(top_loop); MeshConstraintUtil.ConstrainVtxLoopTo(r.Constraints, r.Mesh, top_loop, curve_target); DMeshAABBTree3 spatial = new DMeshAABBTree3(Mesh); spatial.Build(); MeshProjectionTarget target = new MeshProjectionTarget(Mesh, spatial); r.SetProjectionTarget(target); bool bRemesh = true; if (bRemesh) { r.Precompute(); r.EnableFlips = r.EnableSplits = r.EnableCollapses = true; r.MinEdgeLength = target_edge_len; r.MaxEdgeLength = 2 * target_edge_len; r.EnableSmoothing = true; r.SmoothSpeedT = 1.0f; for (int k = 0; k < 40; ++k) { r.BasicRemeshPass(); } r.SetProjectionTarget(null); r.SmoothSpeedT = 0.25f; for (int k = 0; k < 10; ++k) { r.BasicRemeshPass(); } Debug.Assert(Mesh.CheckValidity()); r.BackPropropagate(); } // smooth around the join region to clean up ugliness smooth_region(Mesh, r.Region.BaseBorderV, 3); }
protected virtual DMesh3 compute_standard() { DMesh3 sourceMesh = MeshSource.GetDMeshUnsafe(); ISpatial sourceSpatial = MeshSource.GetSpatial(); DMesh3 meshIn = new DMesh3(sourceMesh); RemesherPro remesh = new RemesherPro(meshIn); //Remesher remesh = new Remesher(meshIn); remesh.SetTargetEdgeLength(TargetEdgeLength); remesh.PreventNormalFlips = this.PreventNormalFlips; remesh.EnableFlips = this.EnableFlips; remesh.EnableSplits = this.EnableSplits; remesh.EnableCollapses = this.EnableCollapses; remesh.EnableSmoothing = this.EnableSmoothing; remesh.SmoothSpeedT = this.SmoothingSpeed; if (ReprojectToInput) { MeshProjectionTarget target = new MeshProjectionTarget(sourceMesh, sourceSpatial); remesh.SetProjectionTarget(target); } // if we are preserving creases, this will also automatically constrain boundary // edges boundary loops/spans. if (preserve_creases) { if (remesh.Constraints == null) { remesh.SetExternalConstraints(new MeshConstraints()); } MeshTopology topo = new MeshTopology(meshIn); topo.CreaseAngle = this.CreaseAngle; topo.AddRemeshConstraints(remesh.Constraints); // replace boundary edge constraints if we want other behaviors if (BoundaryMode == BoundaryModes.FixedBoundaries) { MeshConstraintUtil.FixEdges(remesh.Constraints, meshIn, topo.BoundaryEdges); } } else if (sourceMesh.CachedIsClosed == false) { if (remesh.Constraints == null) { remesh.SetExternalConstraints(new MeshConstraints()); } if (BoundaryMode == BoundaryModes.FreeBoundaries) { MeshConstraintUtil.PreserveBoundaryLoops(remesh.Constraints, meshIn); } else if (BoundaryMode == BoundaryModes.FixedBoundaries) { MeshConstraintUtil.FixAllBoundaryEdges(remesh.Constraints, meshIn); } else if (BoundaryMode == BoundaryModes.ConstrainedBoundaries) { MeshConstraintUtil.FixAllBoundaryEdges_AllowSplit(remesh.Constraints, meshIn, 0); } } remesh.Progress = new ProgressCancel(is_invalidated); remesh.FastestRemesh(RemeshRounds, true); //for (int k = 0; k < RemeshRounds; ++k) // remesh.BasicRemeshPass(); // free boundary remesh can leave sliver triangles around the border. clean that up. if (sourceMesh.CachedIsClosed == false && BoundaryMode == BoundaryModes.FreeBoundaries) { MeshEditor.RemoveFinTriangles(meshIn, (mesh, tid) => { Index3i tv = mesh.GetTriangle(tid); return(MathUtil.AspectRatio(mesh.GetVertex(tv.a), mesh.GetVertex(tv.b), mesh.GetVertex(tv.c)) > 2); }); } if (is_invalidated()) { return(null); } return(meshIn); }
protected override void SolveInstance(IGH_DataAccess DA) { DMesh3_goo dMsh_goo = null; List <Point3d> points = new List <Point3d>(); double targetL = 0; int numI = 0; int fixB = 0; bool projBack = false; double smooth = 0; DA.GetData(0, ref dMsh_goo); DA.GetDataList(2, points); DA.GetData(1, ref targetL); DA.GetData(6, ref numI); DA.GetData(3, ref fixB); DA.GetData(5, ref projBack); DA.GetData(7, ref smooth); List <EdgeConstraint_goo> edgeC = new List <EdgeConstraint_goo>(); DA.GetDataList(4, edgeC); DMesh3 dMsh_copy = new DMesh3(dMsh_goo.Value); Remesher r = new Remesher(dMsh_copy); r.PreventNormalFlips = true; r.SetTargetEdgeLength(targetL); r.SmoothSpeedT = smooth; if (fixB == 2) { MeshConstraintUtil.FixAllBoundaryEdges(r); } else if (fixB == 1) { MeshConstraintUtil.PreserveBoundaryLoops(r); } else { r.SetExternalConstraints(new MeshConstraints()); } if (edgeC.Count > 0) { for (int i = 0; i < edgeC.Count; i++) { var tempEC = edgeC[i]; IProjectionTarget target = new DCurveProjectionTarget(tempEC.crv); for (int j = 0; j < tempEC.edges.Length; j++) { tempEC.constraint.Target = target; r.Constraints.SetOrUpdateEdgeConstraint(tempEC.edges[j], tempEC.constraint); } for (int j = 0; j < tempEC.vertices.Length; j++) { if (tempEC.PinVerts) { r.Constraints.SetOrUpdateVertexConstraint(tempEC.vertices[j], VertexConstraint.Pinned); } else { r.Constraints.SetOrUpdateVertexConstraint(tempEC.vertices[j], new VertexConstraint(target)); } } } } if (points.Count > 0) { DMeshAABBTree3 mshAABB = new DMeshAABBTree3(dMsh_copy, true); var v3pts = points.Select(pt => pt.ToVec3d()); foreach (var p in v3pts) { int id = mshAABB.FindNearestVertex(p, 0.1); if (id != -1) { r.Constraints.SetOrUpdateVertexConstraint(id, VertexConstraint.Pinned); } } } if (projBack) { r.SetProjectionTarget(MeshProjectionTarget.Auto(dMsh_goo.Value)); } for (int k = 0; k < numI; ++k) { r.BasicRemeshPass(); } bool isValid = dMsh_copy.CheckValidity(); if (!isValid) { this.AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Mesh seems to have been corrupted during remeshing. Please check..."); } DA.SetData(0, dMsh_copy); }
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); }
protected virtual DMesh3 compute_bounded_distance() { DMesh3 sourceMesh = MeshSource.GetDMeshUnsafe(); ISpatial inputSpatial = MeshSource.GetSpatial(); DMesh3 targetMesh = TargetSource.GetDMeshUnsafe(); ISpatial targetSpatial = TargetSource.GetSpatial(); double max_dist = (TargetMaxDistance == double.MaxValue) ? double.MaxValue : TargetMaxDistance; DMesh3 meshIn = new DMesh3(sourceMesh); bool target_closed = targetMesh.IsClosed(); MeshVertexSelection roiV = new MeshVertexSelection(meshIn); SpinLock roi_lock = new SpinLock(); gParallel.ForEach(meshIn.VertexIndices(), (vid) => { Vector3d pos = meshIn.GetVertex(vid); Vector3d posTarget = TransformToTarget.TransformP(pos); double dist = MeshQueries.NearestPointDistance(targetMesh, targetSpatial, posTarget, max_dist); bool inside = (target_closed && targetSpatial.IsInside(posTarget)); if (dist < max_dist || inside) { bool taken = false; roi_lock.Enter(ref taken); roiV.Select(vid); roi_lock.Exit(); } }); if (is_invalidated()) { return(null); } MeshFaceSelection roi_faces = new MeshFaceSelection(meshIn, roiV, 1); roi_faces.ExpandToOneRingNeighbours(3); roi_faces.LocalOptimize(); if (is_invalidated()) { return(null); } RegionOperator op = new RegionOperator(meshIn, roi_faces); DMesh3 meshROI = op.Region.SubMesh; if (is_invalidated()) { return(null); } RemesherPro remesher = new RemesherPro(meshROI); remesher.SetTargetEdgeLength(TargetEdgeLength); remesher.PreventNormalFlips = this.PreventNormalFlips; remesher.EnableFlips = this.EnableFlips; remesher.EnableSplits = this.EnableSplits; remesher.EnableCollapses = this.EnableCollapses; remesher.EnableSmoothing = this.EnableSmoothing; remesher.SmoothSpeedT = this.SmoothingSpeed; BoundedProjectionTarget target = new BoundedProjectionTarget() { Source = sourceMesh, SourceSpatial = inputSpatial, Target = targetMesh, TargetSpatial = targetSpatial, SourceToTargetXForm = source_to_target, TargetToSourceXForm = target_to_source, MaxDistance = max_dist, Smoothness = transition_smoothness }; remesher.SetProjectionTarget(target); if (remesher.Constraints == null) { remesher.SetExternalConstraints(new MeshConstraints()); } MeshConstraintUtil.FixAllBoundaryEdges(remesher.Constraints, meshROI); if (is_invalidated()) { return(null); } remesher.Progress = new ProgressCancel(is_invalidated); remesher.FastestRemesh(RemeshRounds); if (is_invalidated()) { return(null); } op.BackPropropagate(); return(meshIn); }
protected override void SolveInstance(IGH_DataAccess DA) { DMesh3_goo dMsh_goo = null; double targetL = 0; bool fixB = false; bool projBack = false; bool run = false; bool reset = false; int maxIter = 0; DA.GetData(0, ref dMsh_goo); DA.GetData(1, ref targetL); DA.GetData(3, ref fixB); DA.GetData(4, ref projBack); DA.GetData(2, ref maxIter); DA.GetData(5, ref run); DA.GetData(6, ref reset); if (passes >= maxIter) { run = false; } if (r is null || reset) { dMsh_copy = new DMesh3(dMsh_goo.Value); r = new Remesher(dMsh_copy); r.PreventNormalFlips = true; r.SetTargetEdgeLength(targetL); r.SmoothSpeedT = 0.5; passes = 0; if (fixB) { MeshConstraintUtil.FixAllBoundaryEdges(r); } if (projBack) { r.SetProjectionTarget(MeshProjectionTarget.Auto(dMsh_goo.Value)); } } if (run && !reset) { r.BasicRemeshPass(); passes++; Update(); } bool isValid = dMsh_copy.CheckValidity(); if (!isValid) { this.AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Mesh seems to have been corrupted during remeshing. Please check..."); } this.Message = "Pass: " + passes.ToString(); DA.SetData(0, dMsh_copy); }
protected override void SolveInstance(IGH_DataAccess DA) { Mesh m = DA.Fetch <Mesh>("Mesh"); bool fixEdges = DA.Fetch <bool>("FixEdges"); double l = DA.Fetch <double>("EdgeLength"); int iterations = DA.Fetch <int>("Iterations"); List <Point3d> fixPt = DA.FetchList <Point3d>("FixPt"); bool project = DA.Fetch <bool>("Project"); bool loops = DA.Fetch <bool>("Loops"); Mesh mesh = m.DuplicateMesh(); mesh.Faces.ConvertQuadsToTriangles(); double len = (l == 0) ? mesh.GetBoundingBox(false).Diagonal.Length * 0.1 : l; //r.PreventNormalFlips = true; List <int> ids = new List <int>(); Point3d[] pts = mesh.Vertices.ToPoint3dArray(); foreach (Point3d p in fixPt) { ids.Add(NGonsCore.PointUtil.ClosestPoint(p, pts)); } DMesh3 dmesh = mesh.ToDMesh3(); Remesher r = new Remesher(dmesh); r.Precompute(); r.SetTargetEdgeLength(len); r.SmoothSpeedT = 0.5; if (project) { r.SetProjectionTarget(MeshProjectionTarget.Auto(dmesh)); } r.EnableFlips = r.EnableSplits = r.EnableCollapses = true; r.EnableSmoothing = true; MeshConstraints cons = new MeshConstraints(); if (ids.Count > 0) { foreach (int id in ids) { //cons.SetOrUpdateVertexConstraint(id, new VertexConstraint(true, 1)); cons.SetOrUpdateVertexConstraint(id, VertexConstraint.Pinned); } } r.SetExternalConstraints(cons); r.Precompute(); if (fixEdges) { //r.SetExternalConstraints(new MeshConstraints()); MeshConstraintUtil.FixAllBoundaryEdges(r); MeshConstraintUtil.FixAllBoundaryEdges_AllowSplit(cons, dmesh, 0); //MeshConstraintUtil.FixAllBoundaryEdges_AllowCollapse(cons, dmesh, 0); } if (loops) { MeshConstraintUtil.PreserveBoundaryLoops(r); //project to edge //MeshConstraintUtil.PreserveBoundaryLoops(cons,dmesh);//project to edge } r.SetExternalConstraints(cons); for (int k = 0; k < iterations; ++k) { r.BasicRemeshPass(); } //output if (ids.Count > 0 && !fixEdges) { this.Message = "Vertices"; } else if (ids.Count == 0 && fixEdges) { this.Message = "Edges"; } else if (ids.Count > 0 && fixEdges) { this.Message = "Vertices + Edges"; } else { this.Message = ""; } dmesh = new DMesh3(dmesh, true); Mesh rmesh = dmesh.ToRhinoMesh(); if (loops) { Mesh mesh_ = rmesh.DuplicateMesh(); Rhino.IndexPair[] closestEdges = new Rhino.IndexPair[fixPt.Count]; int counter = 0; foreach (Point3d p in fixPt) { double[] d = new double[rmesh.TopologyEdges.Count]; int[] eid = new int[rmesh.TopologyEdges.Count]; for (int i = 0; i < rmesh.TopologyEdges.Count; i++) { if (rmesh.TopologyEdges.GetConnectedFaces(i).Length == 1) { Line line = rmesh.TopologyEdges.EdgeLine(i); line.ClosestPoint(p, true); d[i] = line.ClosestPoint(p, true).DistanceToSquared(p); //line.From.DistanceToSquared(p) + line.To.DistanceToSquared(p); } else { d[i] = 99999; } eid[i] = i; } Array.Sort(d, eid); closestEdges[counter++] = rmesh.TopologyEdges.GetTopologyVertices(eid[0]); } for (int i = 0; i < fixPt.Count; i++) { mesh_.Vertices.Add(fixPt[i]); mesh_.Faces.AddFace(rmesh.Vertices.Count + i, closestEdges[i].I, closestEdges[i].J); } rmesh = mesh_; } rmesh.UnifyNormals(); rmesh.RebuildNormals(); // rmesh.UnifyNormals(); DA.SetData(0, rmesh); }