public static RegionRemesher QuickRemesh(DMesh3 mesh, int[] tris, double targetEdgeLen, double smoothSpeed, int rounds, IProjectionTarget target, QuickRemeshFlags flags = QuickRemeshFlags.PreventNormalFlips) { var remesh = new RegionRemesher(mesh, tris); if (target != null) { remesh.SetProjectionTarget(target); } remesh.SetTargetEdgeLength(targetEdgeLen); remesh.SmoothSpeedT = smoothSpeed; if ((flags & QuickRemeshFlags.PreventNormalFlips) != 0) { remesh.PreventNormalFlips = true; } for (int k = 0; k < rounds; ++k) { remesh.BasicRemeshPass(); } remesh.BackPropropagate(); return(remesh); }
protected void project_vertex(int vID, IProjectionTarget targetIn) { Vector3d curpos = mesh.GetVertex(vID); Vector3d projected = targetIn.Project(curpos, vID); mesh.SetVertex(vID, projected); }
public static void ConstrainVtxSpanTo(Remesher r, int[] spanV, IProjectionTarget target, int setID = -1) { if (r.Constraints == null) { r.SetExternalConstraints(new MeshConstraints()); } ConstrainVtxSpanTo(r.Constraints, r.Mesh, spanV, target); }
public static void ConstrainVtxLoopTo(Remesher r, int[] loopV, IProjectionTarget target) { if (r.Constraints == null) { r.SetExternalConstraints(new MeshConstraints()); } ConstrainVtxLoopTo(r.Constraints, r.Mesh, loopV, target); }
public static RegionRemesher QuickRemesh(DMesh3 mesh, int[] tris, double targetEdgeLen, double smoothSpeed, int rounds, IProjectionTarget target) { RegionRemesher remesh = new RegionRemesher(mesh, tris); if (target != null) { remesh.SetProjectionTarget(target); } remesh.SetTargetEdgeLength(targetEdgeLen); remesh.SmoothSpeedT = smoothSpeed; for (int k = 0; k < rounds; ++k) { remesh.BasicRemeshPass(); } remesh.BackPropropagate(); return(remesh); }
public static RegionRemesher QuickRemesh(NGonsCore.geometry3Sharp.mesh.DMesh3 mesh, int[] tris, double minEdgeLen, double maxEdgeLen, double smoothSpeed, int rounds, IProjectionTarget target) { RegionRemesher remesh = new RegionRemesher(mesh, tris); if (target != null) { remesh.SetProjectionTarget(target); } remesh.MinEdgeLength = minEdgeLen; remesh.MaxEdgeLength = maxEdgeLen; remesh.SmoothSpeedT = smoothSpeed; for (int k = 0; k < rounds; ++k) { remesh.BasicRemeshPass(); } remesh.BackPropropagate(); return(remesh); }
// for all vertices in loopV, constrain to target // for all edges in loopV, disable flips and constrain to target public static void ConstrainVtxSpanTo(MeshConstraints cons, DMesh3 mesh, IList <int> spanV, IProjectionTarget target, int setID = -1) { VertexConstraint vc = new VertexConstraint(target); int N = spanV.Count; for (int i = 0; i < N; ++i) { cons.SetOrUpdateVertexConstraint(spanV[i], vc); } EdgeConstraint ec = new EdgeConstraint(EdgeRefineFlags.NoFlip, target); ec.TrackingSetID = setID; for (int i = 0; i < N - 1; ++i) { int v0 = spanV[i]; int v1 = spanV[i + 1]; int eid = mesh.FindEdge(v0, v1); Debug.Assert(eid != DMesh3.InvalidID); if (eid != DMesh3.InvalidID) { cons.SetOrUpdateEdgeConstraint(eid, ec); } } }
public static Action <List <Vector3d> > MakeLoopOnSurfaceProcessorF(FScene scene, IProjectionTarget surface, Func <float> SampleRateF, Func <bool> ClosedF, float fSmoothAlpha = 0.2f, int nSmoothIter = 15) { return((vertices) => { CurveResampler resampler = new CurveResampler(); IWrappedCurve3d temp_curve = new IWrappedCurve3d() { VertexList = vertices, Closed = ClosedF() }; float rate = SampleRateF(); List <Vector3d> result = resampler.SplitCollapseResample(temp_curve, rate, rate * 0.6); if (result != null && result.Count > 3) { vertices.Clear(); vertices.AddRange(result); } CurveUtils.InPlaceSmooth(vertices, fSmoothAlpha, nSmoothIter, ClosedF()); gs.CurveDrawingUtil.ProjectToTarget_Scene(vertices, scene, surface); }); }
public static void ProjectToTarget_Scene(IList <Vector3d> vertices, FScene scene, IProjectionTarget target) { int N = vertices.Count; for (int i = 0; i < N; ++i) { Vector3f v = (Vector3f)vertices[i]; Vector3f vW = SceneTransforms.SceneToWorldP(scene, v); vW = (Vector3f)target.Project(vW); vertices[i] = SceneTransforms.WorldToSceneP(scene, vW); } }
public VertexConstraint(IProjectionTarget target) { Fixed = false; FixedSetID = InvalidSetID; Target = target; }
public IProjectionTarget Target; // vertex is constrained to lie on this projection Target. // Fixed and Target are mutually exclusive public VertexConstraint(bool isFixed, int setID = InvalidSetID) { Fixed = isFixed; FixedSetID = setID; Target = null; }
public EdgeConstraint(EdgeRefineFlags rflags, IProjectionTarget target) { refineFlags = rflags; Target = target; }
public IProjectionTarget Target; // edge is associated with this projection Target. // Currently only used as information, we do not explicitly // project edges onto targets (must also set VertexConstraint) public EdgeConstraint(EdgeRefineFlags rflags) { refineFlags = rflags; Target = null; }
protected void merge_loops(DMesh3 mesh, EdgeLoop cutLoop, EdgeLoop connectorLoop, bool is_outer) { /* * To join the loops, we are going to first make a circle, then snap both * open loops to that circle. Then we sample a set of vertices on the circle * and remesh the loops while also snapping them to the circle vertices. * The result is two perfectly-matching edge loops. */ //AxisAlignedBox3d cutLoopBounds = // BoundsUtil.Bounds(cutLoop.Vertices, (vid) => { return mesh.GetVertex(vid); }); AxisAlignedBox3d cutLoopBounds = cutLoop.GetBounds(); AxisAlignedBox3d connectorLoopBounds = connectorLoop.GetBounds(); Vector3d midPt = (cutLoopBounds.Center + connectorLoopBounds.Center) * 0.5; double midY = midPt.y; // this mess construcst the circle and the sampled version Frame3f circFrame = new Frame3f(midPt); Circle3d circ = new Circle3d(circFrame, connectorLoopBounds.Width * 0.5, 1); DistPoint3Circle3 dist = new DistPoint3Circle3(Vector3d.Zero, circ); DCurve3 sampled = new DCurve3(); double target_edge_len = TargetMeshEdgeLength; int N = (int)(circ.ArcLength / target_edge_len); for (int k = 0; k < N; ++k) { sampled.AppendVertex(circ.SampleT((double)k / (double)N)); } MergeProjectionTarget circleTarget = new MergeProjectionTarget() { Mesh = mesh, CircleDist = dist, CircleLoop = sampled }; EdgeLoop[] loops = new EdgeLoop[2] { cutLoop, connectorLoop }; EdgeLoop[] outputLoops = new EdgeLoop[2]; // loops after this remeshing/etc (but might be missing some verts/edges!) for (int li = 0; li < 2; ++li) { EdgeLoop loop = loops[li]; // snap the loop verts onto the analytic circle foreach (int vid in loop.Vertices) { Vector3d v = mesh.GetVertex(vid); dist.Point = new Vector3d(v.x, midY, v.z); mesh.SetVertex(vid, dist.Compute().CircleClosest); } if (DebugStep <= 5) { continue; } // remesh around the edge loop while we snap it to the sampled circle verts EdgeLoopRemesher loopRemesh = new EdgeLoopRemesher(mesh, loop) { LocalSmoothingRings = 3 }; loopRemesh.EnableParallelProjection = false; loopRemesh.SetProjectionTarget(circleTarget); loopRemesh.SetTargetEdgeLength(TargetMeshEdgeLength); loopRemesh.SmoothSpeedT = 0.5f; for (int k = 0; k < 5; ++k) { loopRemesh.BasicRemeshPass(); } loopRemesh.SmoothSpeedT = 0; for (int k = 0; k < 2; ++k) { loopRemesh.BasicRemeshPass(); } EdgeLoop newLoop = loopRemesh.OutputLoop; outputLoops[li] = newLoop; if (DebugStep <= 6) { continue; } // hard snap the loop vertices to the sampled circle verts foreach (int vid in newLoop.Vertices) { Vector3d v = mesh.GetVertex(vid); v = circleTarget.Project(v, vid); mesh.SetVertex(vid, v); } // [TODO] we could re-order newLoop verts/edges to match the sampled verts order, // then the pair of loops would be consistently ordered (currently no guarantee) if (DebugStep <= 7) { continue; } // collapse any degenerate edges on loop (just in case) // DANGER: if this actually happens, then outputLoops[li] has some invalid verts/edges! foreach (int eid in newLoop.Edges) { if (mesh.IsEdge(eid)) { Index2i ev = mesh.GetEdgeV(eid); Vector3d a = mesh.GetVertex(ev.a), b = mesh.GetVertex(ev.b); if (a.Distance(b) < TargetMeshEdgeLength * 0.001) { DMesh3.EdgeCollapseInfo collapse; mesh.CollapseEdge(ev.a, ev.b, out collapse); } } } } if (DebugStep <= 7) { return; } /* * Ok now we want to merge the loops and make them nice */ // would be more efficient to find loops and stitch them... MergeCoincidentEdges merge = new MergeCoincidentEdges(mesh); merge.Apply(); // fill any fail-holes?? // remesh merge region MeshVertexSelection remesh_roi_v = new MeshVertexSelection(mesh); remesh_roi_v.Select(outputLoops[0].Vertices); remesh_roi_v.Select(outputLoops[1].Vertices); remesh_roi_v.ExpandToOneRingNeighbours(5); MeshFaceSelection remesh_roi = new MeshFaceSelection(mesh, remesh_roi_v, 1); remesh_roi.LocalOptimize(true, true); IProjectionTarget projTarget = null; if (is_outer) { projTarget = new NoPenetrationProjectionTarget() { Spatial = this.OuterOffsetMeshSpatial }; } else { projTarget = new NoPenetrationProjectionTarget() { Spatial = this.InnerOffsetMeshSpatial }; } RegionRemesher join_remesh = RegionRemesher.QuickRemesh(mesh, remesh_roi.ToArray(), TargetMeshEdgeLength, 0.5, 5, projTarget); if (DebugStep <= 8) { return; } if (false && is_outer) { Func <int, bool> filterF = (tid) => { return(mesh.GetTriCentroid(tid).y > connectorLoopBounds.Max.y); }; MeshFaceSelection tris = new MeshFaceSelection(mesh); foreach (int tid in join_remesh.CurrentBaseTriangles) { if (filterF(tid)) { tris.Select(tid); } } tris.ExpandToOneRingNeighbours(5, filterF); MeshVertexSelection verts = new MeshVertexSelection(mesh, tris); MeshIterativeSmooth smooth = new MeshIterativeSmooth(mesh, verts.ToArray(), true); smooth.Alpha = 1.0f; smooth.Rounds = 25; smooth.ProjectF = (v, n, vid) => { return(projTarget.Project(v)); }; smooth.Smooth(); } // [RMS] this smooths too far. we basically only want to smooth 'up' from top of socket... //LaplacianMeshSmoother.RegionSmooth(mesh, join_remesh.CurrentBaseTriangles, 1, 10); // need to post-enforce thickness, which we aren't doing above - we could though }
public void SetProjectionTarget(IProjectionTarget target) { this.target = target; }
// After we split an edge, we have created a new edge and a new vertex. // The edge needs to inherit the constraint on the other pre-existing edge that we kept. // In addition, if the edge vertices were both constrained, then we /might/ // want to also constrain this new vertex, possibly project to constraint target. protected virtual void update_after_split(int edgeID, int va, int vb, ref DMesh3.EdgeSplitInfo splitInfo) { bool bPositionFixed = false; if (constraints != null && constraints.HasEdgeConstraint(edgeID)) { // inherit edge constraint constraints.SetOrUpdateEdgeConstraint(splitInfo.eNewBN, constraints.GetEdgeConstraint(edgeID)); // [RMS] update vertex constraints. Note that there is some ambiguity here. // Both verts being constrained doesn't inherently mean that the edge is on // a constraint, that's why these checks are only applied if edge is constrained. // But constrained edge doesn't necessarily mean we want to inherit vert constraints!! // // although, pretty safe to assume that we would at least disable flips // if both vertices are constrained to same line/curve. So, maybe this makes sense... // // (perhaps edge constraint should be explicitly tagged to resolve this ambiguity??) // vert inherits Fixed if both orig edge verts Fixed, and both tagged with same SetID VertexConstraint ca = constraints.GetVertexConstraint(va); VertexConstraint cb = constraints.GetVertexConstraint(vb); if (ca.Fixed && cb.Fixed) { int nSetID = (ca.FixedSetID > 0 && ca.FixedSetID == cb.FixedSetID) ? ca.FixedSetID : VertexConstraint.InvalidSetID; constraints.SetOrUpdateVertexConstraint(splitInfo.vNew, new VertexConstraint(true, nSetID)); bPositionFixed = true; } // vert inherits Target if: // 1) both source verts and edge have same Target, and is same as edge target // 2) either vert has same target as edge, and other vert is fixed if (ca.Target != null || cb.Target != null) { IProjectionTarget edge_target = constraints.GetEdgeConstraint(edgeID).Target; IProjectionTarget set_target = null; if (ca.Target == cb.Target && ca.Target == edge_target) { set_target = edge_target; } else if (ca.Target == edge_target && cb.Fixed) { set_target = edge_target; } else if (cb.Target == edge_target && ca.Fixed) { set_target = edge_target; } if (set_target != null) { constraints.SetOrUpdateVertexConstraint(splitInfo.vNew, new VertexConstraint(set_target)); project_vertex(splitInfo.vNew, set_target); bPositionFixed = true; } } } if (EnableInlineProjection && bPositionFixed == false && target != null) { project_vertex(splitInfo.vNew, target); } }
public EdgeConstraint(EdgeRefineFlags rflags, IProjectionTarget target) { refineFlags = rflags; Target = target; TrackingSetID = -1; }
public int TrackingSetID; // not actually a constraint, but allows is to find descendents // of an constraind input edge public EdgeConstraint(EdgeRefineFlags rflags) { refineFlags = rflags; Target = null; TrackingSetID = -1; }
public static void test_remesh_constraints_vertcurves() { int Slices = 16; DMesh3 mesh = TestUtil.MakeCappedCylinder(false, Slices); MeshUtil.ScaleMesh(mesh, Frame3f.Identity, new Vector3f(1, 2, 1)); //DMesh3 mesh = TestUtil.MakeRemeshedCappedCylinder(0.25); //DMesh3 mesh = TestUtil.MakeRemeshedCappedCylinder(1.0); mesh.CheckValidity(); AxisAlignedBox3d bounds = mesh.CachedBounds; // construct mesh projection target DMesh3 meshCopy = new DMesh3(mesh); meshCopy.CheckValidity(); DMeshAABBTree3 tree = new DMeshAABBTree3(meshCopy); tree.Build(); MeshProjectionTarget mesh_target = new MeshProjectionTarget() { Mesh = meshCopy, Spatial = tree }; // cylinder projection target CylinderProjectionTarget cyl_target = new CylinderProjectionTarget() { Cylinder = new Cylinder3d(new Vector3d(0, 1, 0), Vector3d.AxisY, 1, 2) }; //IProjectionTarget target = mesh_target; IProjectionTarget target = cyl_target; // construct projection target circles CircleProjectionTarget bottomCons = new CircleProjectionTarget() { Circle = new Circle3d(bounds.Center, 1.0) }; bottomCons.Circle.Center.y = bounds.Min.y; CircleProjectionTarget topCons = new CircleProjectionTarget() { Circle = new Circle3d(bounds.Center, 1.0) }; topCons.Circle.Center.y = bounds.Max.y; if (WriteDebugMeshes) { TestUtil.WriteDebugMesh(mesh, "remesh_analytic_constraints_test_before.obj"); } // construct constraint set MeshConstraints cons = new MeshConstraints(); //EdgeRefineFlags useFlags = EdgeRefineFlags.NoFlip | EdgeRefineFlags.NoCollapse; EdgeRefineFlags useFlags = EdgeRefineFlags.NoFlip; bool bConstrainVertices = true; foreach (int eid in mesh.EdgeIndices()) { double fAngle = MeshUtil.OpeningAngleD(mesh, eid); if (fAngle > 30.0f) { Index2i ev = mesh.GetEdgeV(eid); Vector3d ev0 = mesh.GetVertex(ev[0]); Vector3d ev1 = mesh.GetVertex(ev[1]); CircleProjectionTarget loopTarget = null; if (ev0.y > bounds.Center.y && ev1.y > bounds.Center.y) { loopTarget = topCons; } else if (ev0.y < bounds.Center.y && ev1.y < bounds.Center.y) { loopTarget = bottomCons; } cons.SetOrUpdateEdgeConstraint(eid, new EdgeConstraint(useFlags, loopTarget)); if (bConstrainVertices && loopTarget != null) { cons.SetOrUpdateVertexConstraint(ev[0], new VertexConstraint(loopTarget)); cons.SetOrUpdateVertexConstraint(ev[1], new VertexConstraint(loopTarget)); } } } Remesher r = new Remesher(mesh); //r.SetExternalConstraints(cons); r.SetProjectionTarget(target); r.Precompute(); r.ENABLE_PROFILING = true; var stopwatch = Stopwatch.StartNew(); //double fResScale = 1.0f; double fResScale = 0.5f; r.EnableFlips = r.EnableSplits = r.EnableCollapses = true; r.MinEdgeLength = 0.1f * fResScale; r.MaxEdgeLength = 0.2f * fResScale; r.EnableSmoothing = true; r.SmoothSpeedT = 1.0f; try { for (int k = 0; k < 20; ++k) { r.BasicRemeshPass(); mesh.CheckValidity(); } } catch { // continue; } stopwatch.Stop(); System.Console.WriteLine("Second Pass Timing: " + stopwatch.Elapsed); if (WriteDebugMeshes) { TestUtil.WriteDebugMesh(mesh, "remesh_analytic_constraints_test_after.obj"); } }
// for all vertices in loopV, constrain to target // for all edges in loopV, disable flips and constrain to target public static void ConstrainVtxLoopTo(MeshConstraints cons, DMesh3 mesh, int[] loopV, IProjectionTarget target, int setID = -1) { VertexConstraint vc = new VertexConstraint(target); for (int i = 0; i < loopV.Length; ++i) { cons.SetOrUpdateVertexConstraint(loopV[i], vc); } EdgeConstraint ec = new EdgeConstraint(EdgeRefineFlags.NoFlip, target); ec.TrackingSetID = setID; for (int i = 0; i < loopV.Length; ++i) { int v0 = loopV[i]; int v1 = loopV[(i + 1) % loopV.Length]; int eid = mesh.FindEdge(v0, v1); Debug.Assert(eid != DMesh3.InvalidID); if (eid != DMesh3.InvalidID) { cons.SetOrUpdateEdgeConstraint(eid, ec); } } }
public DMesh3 remesh_constraints_vertcurves(int iterations, DMesh3 mesh, double min, double max, double angle) { mesh.CheckValidity(); AxisAlignedBox3d bounds = mesh.CachedBounds; // construct mesh projection target DMesh3 meshCopy = new DMesh3(mesh); meshCopy.CheckValidity(); DMeshAABBTree3 tree = new DMeshAABBTree3(meshCopy); tree.Build(); MeshProjectionTarget mesh_target = new MeshProjectionTarget() { Mesh = meshCopy, Spatial = tree }; // cylinder projection target CylinderProjectionTarget cyl_target = new CylinderProjectionTarget() { Cylinder = new Cylinder3d(new Vector3D(0, 1, 0), Vector3D.AxisY, 1, 2) }; //IProjectionTarget target = mesh_target; IProjectionTarget target = cyl_target; // construct projection target circles CircleProjectionTarget bottomCons = new CircleProjectionTarget() { Circle = new Circle3d(bounds.Center, 1.0) }; bottomCons.Circle.Center.y = bounds.Min.y; CircleProjectionTarget topCons = new CircleProjectionTarget() { Circle = new Circle3d(bounds.Center, 1.0) }; topCons.Circle.Center.y = bounds.Max.y; // construct constraint set MeshConstraints cons = new MeshConstraints(); //EdgeRefineFlags useFlags = EdgeRefineFlags.NoFlip | EdgeRefineFlags.NoCollapse; EdgeRefineFlags useFlags = EdgeRefineFlags.NoFlip; bool bConstrainVertices = true; foreach (int eid in mesh.EdgeIndices()) { double fAngle = MeshUtil.OpeningAngleD(mesh, eid); if (fAngle > 30.0f) { Index2i ev = mesh.GetEdgeV(eid); Vector3D ev0 = mesh.GetVertex(ev[0]); Vector3D ev1 = mesh.GetVertex(ev[1]); CircleProjectionTarget loopTarget = null; if (ev0.y > bounds.Center.y && ev1.y > bounds.Center.y) { loopTarget = topCons; } else if (ev0.y < bounds.Center.y && ev1.y < bounds.Center.y) { loopTarget = bottomCons; } cons.SetOrUpdateEdgeConstraint(eid, new EdgeConstraint(useFlags, loopTarget)); if (bConstrainVertices && loopTarget != null) { cons.SetOrUpdateVertexConstraint(ev[0], new VertexConstraint(loopTarget)); cons.SetOrUpdateVertexConstraint(ev[1], new VertexConstraint(loopTarget)); } } } Remesher r = new Remesher(mesh); //r.SetExternalConstraints(cons); r.SetProjectionTarget(target); r.Precompute(); r.ENABLE_PROFILING = true; r.EnableFlips = r.EnableSplits = r.EnableCollapses = true; r.MinEdgeLength = min; r.MaxEdgeLength = max; r.EnableSmoothing = true; r.SmoothSpeedT = 1.0f; for (int k = 0; k < iterations; ++k) { r.BasicRemeshPass(); mesh.CheckValidity(); } return(mesh); }