void compute_trimmed_mesh() { // curve is on base leg, map to deformed leg // [TODO] really should be doing this via deformation, rather than nearest-point DCurve3 curve = new DCurve3(CurveSource.GetICurve()); for (int i = 0; i < curve.VertexCount; ++i) { curve[i] = MeshQueries.NearestPointFrame(cachedInputMesh, cachedInputMeshSpatial, curve[i]).Origin; } TrimmedMesh = new DMesh3(cachedInputMesh); TrimmedMesh.EnableTriangleGroups(0); AxisAlignedBox3d bounds = TrimmedMesh.CachedBounds; // try to find seed based on raycast, which doesn't always work. // Note that seed is the seed for the *eroded* region, not the kept region Vector3d basePt = bounds.Center + 10 * bounds.Extents.y * Vector3d.AxisY; int hit_tid = cachedInputMeshSpatial.FindNearestHitTriangle(new Ray3d(basePt, -Vector3d.AxisY)); Vector3d seed = cachedInputMesh.GetTriCentroid(hit_tid); if (flip_trim_side) { basePt = bounds.Center - 10 * bounds.Extents.y * Vector3d.AxisY; hit_tid = cachedInputMeshSpatial.FindNearestHitTriangle(new Ray3d(basePt, Vector3d.AxisY)); seed = cachedInputMesh.GetTriCentroid(hit_tid); } MeshTrimLoop trim = new MeshTrimLoop(TrimmedMesh, curve, seed, cachedInputMeshSpatial); trim.Trim(); if (TrimmedMesh.HasVertexColors == false) { TrimmedMesh.EnableVertexColors(SocketVertexColor); } else { foreach (int vid in TrimmedMesh.VertexIndices()) { TrimmedMesh.SetVertexColor(vid, SocketVertexColor); } } MeshTransforms.FromFrame(TrimmedMesh, cachedInputsTransform); }
public void Initialize_AutoFitBox() { DMeshSO TargetMeshSO = TargetSO as DMeshSO; // initialize w/ auto-fit box DMesh3 mesh = TargetMeshSO.Mesh; DMeshAABBTree3 spatial = TargetMeshSO.Spatial; meshBounds = mesh.CachedBounds; create_preview_so(); ContOrientedBox3 boxFitter = new ContOrientedBox3( new RemapItr <Vector3d, int>(mesh.TriangleIndices(), (tid) => { return(mesh.GetTriCentroid(tid)); }), new RemapItr <double, int>(mesh.TriangleIndices(), (tid) => { return(mesh.GetTriArea(tid)); })); //DebugUtil.EmitDebugBox("fitbox", boxFitter.Box, Colorf.Red, TargetSO.RootGameObject, false); Box3d fitBox = boxFitter.Box; int longest = 0; if (fitBox.Extent.y > fitBox.Extent.x) { longest = 1; } if (fitBox.Extent.z > fitBox.Extent[longest]) { longest = 2; } Vector3d vTop = fitBox.Center + fitBox.Extent[longest] * fitBox.Axis(longest); Vector3d vBottom = fitBox.Center - fitBox.Extent[longest] * fitBox.Axis(longest); int base_tid = spatial.FindNearestTriangle(vBottom); int top_tid = spatial.FindNearestTriangle(vTop); if (vTop.y < vBottom.y) { int tmp = base_tid; base_tid = top_tid; top_tid = tmp; } Vector3d vBasePt = mesh.GetTriCentroid(base_tid); Vector3d vTopPt = mesh.GetTriCentroid(top_tid); int other1 = (longest + 1) % 3, other2 = (longest + 2) % 3; int front_tid = spatial.FindNearestHitTriangle(new Ray3d(fitBox.Center, fitBox.Axis(other1))); Vector3d vFrontPt = mesh.GetTriCentroid(front_tid); int back_tid = spatial.FindNearestHitTriangle(new Ray3d(fitBox.Center, -fitBox.Axis(other1))); Vector3d vBackPt = mesh.GetTriCentroid(back_tid); int right_tid = spatial.FindNearestHitTriangle(new Ray3d(fitBox.Center, fitBox.Axis(other2))); Vector3d vRightPt = mesh.GetTriCentroid(right_tid); int left_tid = spatial.FindNearestHitTriangle(new Ray3d(fitBox.Center, -fitBox.Axis(other2))); Vector3d vLeftPt = mesh.GetTriCentroid(left_tid); initialFrontPt = (Vector3f)vFrontPt; SetPointPosition_Internal(BasePointID, MeshQueries.SurfaceFrame(mesh, base_tid, vBasePt), CoordSpace.ObjectCoords); SetPointPosition_Internal(FrontPointID, MeshQueries.SurfaceFrame(mesh, front_tid, vFrontPt), CoordSpace.ObjectCoords); SetPointPosition(TopPointID, MeshQueries.SurfaceFrame(mesh, top_tid, vTopPt), CoordSpace.ObjectCoords); }
public void EndStroke() { if (CurrentStroke.Count >= 2) { DMesh3 mesh = Target.Mesh; TransformSequence toScene = SceneTransforms.ObjectToSceneXForm(Target); List <int> tris1 = new List <int>(), tris2 = new List <int>(); Ray3f first = CurrentStroke[0], last = CurrentStroke[CurrentStroke.Count - 1]; Vector3f v0 = PlaneFrameS.RayPlaneIntersection(first.Origin, first.Direction, 2); Vector3f v1 = PlaneFrameS.RayPlaneIntersection(last.Origin, last.Direction, 2); Vector3f planeN = Vector3f.Cross(first.Direction, last.Direction); Frame3f planeF = new Frame3f((v0 + v1) / 2, planeN); foreach (int tid in mesh.TriangleIndices()) { Vector3f c = (Vector3f)mesh.GetTriCentroid(tid); c = toScene.TransformP(c); if (planeF.DistanceToPlaneSigned(c, 2) < 0) { tris1.Add(tid); } else { tris2.Add(tid); } } double area1 = MeshMeasurements.AreaT(mesh, tris1); double area2 = MeshMeasurements.AreaT(mesh, tris2); lastSelection = new MeshFaceSelection(mesh); lastSelection.Select((area1 > area2) ? tris2 : tris1); lastSelection.LocalOptimize(); if (OnStrokeCompletedF != null) { OnStrokeCompletedF(Target, lastSelection); } } CurrentStroke.Clear(); }
public static void test_remove_change_apply() { DMesh3 testMesh = TestUtil.LoadTestInputMesh("bunny_solid.obj"); DMesh3 copy = new DMesh3(testMesh); Vector3d c = testMesh.CachedBounds.Center; MeshFaceSelection selection = new MeshFaceSelection(testMesh); foreach (int tid in testMesh.TriangleIndices()) { if (testMesh.GetTriCentroid(tid).x > c.x) { selection.Select(tid); } } RemoveTrianglesMeshChange change = new RemoveTrianglesMeshChange(); change.InitializeFromApply(testMesh, selection); testMesh.CheckValidity(true); change.Apply(copy); copy.CheckValidity(true); if (!copy.IsSameMesh(testMesh, true)) { System.Console.WriteLine("FAILED copy.IsSameMesh() 1"); } change.Revert(testMesh); testMesh.CheckValidity(false); change.Revert(copy); copy.CheckValidity(false); if (!copy.IsSameMesh(testMesh, true)) { System.Console.WriteLine("FAILED copy.IsSameMesh() 1"); } System.Console.WriteLine("test_remove_change_apply ok"); }
private bool IsInRange(DMesh3 mesh, int triIndexOriginal, int triIndex, double rangeSquared) { var triOriginal = mesh.GetTriCentroid(triIndexOriginal); var tri = mesh.GetTriangle(triIndex); var v1 = mesh.GetVertex(tri.a); var v2 = mesh.GetVertex(tri.b); var v3 = mesh.GetVertex(tri.c); if (v1.DistanceSquared(triOriginal) < rangeSquared) { return(true); } if (v2.DistanceSquared(triOriginal) < rangeSquared) { return(true); } if (v3.DistanceSquared(triOriginal) < rangeSquared) { return(true); } return(false); }
public void InitializeGeodesicDistance(Vector3d source, int source_tid) { DijkstraGraphDistance dist = new DijkstraGraphDistance(Mesh.MaxTriangleID, false, (tid) => { return(Mesh.IsTriangle(tid)); }, (a, b) => { return((float)Mesh.GetTriCentroid(a).Distance(Mesh.GetTriCentroid(b))); }, Mesh.TriTrianglesItr); dist.AddSeed(source_tid, (float)Mesh.GetTriCentroid(source_tid).Distance(source)); //Index3i tri = Mesh.GetTriangle(source_tid); //for (int j = 0; j < 3; ++j) { // dist.AddSeed(tri[j], (float)Mesh.GetVertex(tri[j]).Distance(source)); //} dist.TrackOrder = true; dist.Compute(); List <int> order = dist.GetOrder(); tri_ordering = new OrderedTri[order.Count]; for (int k = 0; k < order.Count; ++k) { tri_ordering[k].tid = order[k]; tri_ordering[k].scalar = dist.GetDistance(order[k]); } tid_to_order_idx = new int[Mesh.MaxTriangleID]; for (int k = 0; k < order.Count; ++k) { tid_to_order_idx[order[k]] = k; } // rebuild chunks data structures update_ordered_chunks(); }
public virtual bool Apply() { DMesh3 testAgainstMesh = Mesh; if (InsideMode == CalculationMode.RayParity) { MeshBoundaryLoops loops = new MeshBoundaryLoops(testAgainstMesh); if (loops.Count > 0) { testAgainstMesh = new DMesh3(Mesh); foreach (var loop in loops) { if (Cancelled()) { return(false); } SimpleHoleFiller filler = new SimpleHoleFiller(testAgainstMesh, loop); filler.Fill(); } } } DMeshAABBTree3 spatial = (Spatial != null && testAgainstMesh == Mesh) ? Spatial : new DMeshAABBTree3(testAgainstMesh, true); if (InsideMode == CalculationMode.AnalyticWindingNumber) { spatial.WindingNumber(Vector3d.Zero); } else if (InsideMode == CalculationMode.FastWindingNumber) { spatial.FastWindingNumber(Vector3d.Zero); } if (Cancelled()) { return(false); } // ray directions List <Vector3d> ray_dirs = null; int NR = 0; if (InsideMode == CalculationMode.SimpleOcclusionTest) { ray_dirs = new List <Vector3d>(); ray_dirs.Add(Vector3d.AxisX); ray_dirs.Add(-Vector3d.AxisX); ray_dirs.Add(Vector3d.AxisY); ray_dirs.Add(-Vector3d.AxisY); ray_dirs.Add(Vector3d.AxisZ); ray_dirs.Add(-Vector3d.AxisZ); NR = ray_dirs.Count; } Func <Vector3d, bool> isOccludedF = (pt) => { if (InsideMode == CalculationMode.RayParity) { return(spatial.IsInside(pt)); } else if (InsideMode == CalculationMode.AnalyticWindingNumber) { return(spatial.WindingNumber(pt) > WindingIsoValue); } else if (InsideMode == CalculationMode.FastWindingNumber) { return(spatial.FastWindingNumber(pt) > WindingIsoValue); } else { for (int k = 0; k < NR; ++k) { int hit_tid = spatial.FindNearestHitTriangle(new Ray3d(pt, ray_dirs[k])); if (hit_tid == DMesh3.InvalidID) { return(false); } } return(true); } }; bool cancel = false; BitArray vertices = null; if (PerVertex) { vertices = new BitArray(Mesh.MaxVertexID); MeshNormals normals = null; if (Mesh.HasVertexNormals == false) { normals = new MeshNormals(Mesh); normals.Compute(); } gParallel.ForEach(Mesh.VertexIndices(), (vid) => { if (cancel) { return; } if (vid % 10 == 0) { cancel = Cancelled(); } Vector3d c = Mesh.GetVertex(vid); Vector3d n = (normals == null) ? Mesh.GetVertexNormal(vid) : normals[vid]; c += n * NormalOffset; vertices[vid] = isOccludedF(c); }); } if (Cancelled()) { return(false); } RemovedT = new List <int>(); SpinLock removeLock = new SpinLock(); gParallel.ForEach(Mesh.TriangleIndices(), (tid) => { if (cancel) { return; } if (tid % 10 == 0) { cancel = Cancelled(); } bool inside = false; if (PerVertex) { Index3i tri = Mesh.GetTriangle(tid); inside = vertices[tri.a] || vertices[tri.b] || vertices[tri.c]; } else { Vector3d c = Mesh.GetTriCentroid(tid); Vector3d n = Mesh.GetTriNormal(tid); c += n * NormalOffset; inside = isOccludedF(c); } if (inside) { bool taken = false; removeLock.Enter(ref taken); RemovedT.Add(tid); removeLock.Exit(); } }); if (Cancelled()) { return(false); } if (RemovedT.Count > 0) { MeshEditor editor = new MeshEditor(Mesh); bool bOK = editor.RemoveTriangles(RemovedT, true); RemoveFailed = (bOK == false); } return(true); }
public bool Insert() { Func <int, bool> is_contained_v = (vid) => { Vector3d v = Mesh.GetVertex(vid); Vector2f vf2 = ProjectFrame.ToPlaneUV((Vector3f)v, 2); return(Polygon.Contains(vf2)); }; MeshVertexSelection vertexROI = new MeshVertexSelection(Mesh); Index3i seedT = Mesh.GetTriangle(SeedTriangle); // if a seed vert of seed triangle is containd in polygon, we will // flood-fill out from there, this gives a better ROI. // If not, we will try flood-fill from the seed triangles. List <int> seed_verts = new List <int>(); for (int j = 0; j < 3; ++j) { if (is_contained_v(seedT[j])) { seed_verts.Add(seedT[j]); } } if (seed_verts.Count == 0) { seed_verts.Add(seedT.a); seed_verts.Add(seedT.b); seed_verts.Add(seedT.c); } // flood-fill out from seed vertices until we have found all vertices // contained in polygon vertexROI.FloodFill(seed_verts.ToArray(), is_contained_v); // convert vertex ROI to face ROI MeshFaceSelection faceROI = new MeshFaceSelection(Mesh, vertexROI, 1); faceROI.ExpandToOneRingNeighbours(); faceROI.FillEars(true); // this might be a good idea... // construct submesh RegionOperator regionOp = new RegionOperator(Mesh, faceROI); DSubmesh3 roiSubmesh = regionOp.Region; DMesh3 roiMesh = roiSubmesh.SubMesh; // save 3D positions of unmodified mesh Vector3d[] initialPositions = new Vector3d[roiMesh.MaxVertexID]; // map roi mesh to plane MeshTransforms.PerVertexTransform(roiMesh, roiMesh.VertexIndices(), (v, vid) => { Vector2f uv = ProjectFrame.ToPlaneUV((Vector3f)v, 2); initialPositions[vid] = v; return(new Vector3d(uv.x, uv.y, 0)); }); // save a copy of 2D mesh and construct bvtree. we will use // this later to project back to 3d // [TODO] can we use a better spatial DS here, that takes advantage of 2D? DMesh3 projectMesh = new DMesh3(roiMesh); DMeshAABBTree3 projecter = new DMeshAABBTree3(projectMesh, true); MeshInsertUVPolyCurve insertUV = new MeshInsertUVPolyCurve(roiMesh, Polygon); //insertUV.Validate() bool bOK = insertUV.Apply(); if (!bOK) { throw new Exception("insertUV.Apply() failed"); } if (SimplifyInsertion) { insertUV.Simplify(); } int[] insertedPolyVerts = insertUV.CurveVertices; // grab inserted loop, assuming it worked EdgeLoop insertedLoop = null; if (insertUV.Loops.Count == 1) { insertedLoop = insertUV.Loops[0]; } // find interior triangles List <int> interiorT = new List <int>(); foreach (int tid in roiMesh.TriangleIndices()) { Vector3d centroid = roiMesh.GetTriCentroid(tid); if (Polygon.Contains(centroid.xy)) { interiorT.Add(tid); } } if (RemovePolygonInterior) { MeshEditor editor = new MeshEditor(roiMesh); editor.RemoveTriangles(interiorT, true); InteriorTriangles = null; } else { InteriorTriangles = interiorT.ToArray(); } // map back to 3d Vector3d a = Vector3d.Zero, b = Vector3d.Zero, c = Vector3d.Zero; foreach (int vid in roiMesh.VertexIndices()) { // [TODO] somehow re-use exact positions from regionOp maps? // construct new 3D pos w/ barycentric interpolation Vector3d v = roiMesh.GetVertex(vid); int tid = projecter.FindNearestTriangle(v); Index3i tri = projectMesh.GetTriangle(tid); projectMesh.GetTriVertices(tid, ref a, ref b, ref c); Vector3d bary = MathUtil.BarycentricCoords(ref v, ref a, ref b, ref c); Vector3d pos = bary.x * initialPositions[tri.a] + bary.y * initialPositions[tri.b] + bary.z * initialPositions[tri.c]; roiMesh.SetVertex(vid, pos); } bOK = BackPropagate(regionOp, insertedPolyVerts, insertedLoop); return(bOK); }
public Vector3d GetVertex(int i) { return(Mesh.GetTriCentroid(i)); }
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 static void test_uv_insert_string() { DMesh3 mesh = TestUtil.LoadTestInputMesh("plane_xy_25x25.obj"); mesh.EnableVertexUVs(Vector2f.Zero); DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh); spatial.Build(); int tid = spatial.FindNearestTriangle(Vector3d.Zero); PolygonFont2d font = PolygonFont2d.ReadFont("c:\\scratch\\font.bin"); //List<GeneralPolygon2d> letter = new List<GeneralPolygon2d>(font.Characters.First().Value.Polygons); //double targetWidth = 20.0f; List <GeneralPolygon2d> letter = font.GetCharacter('a'); double targetWidth = 10.0f; AxisAlignedBox2d bounds = font.MaxBounds; Vector2d center = bounds.Center; Vector2d scale2d = (targetWidth / font.MaxBounds.Width) * new Vector2d(1, 1); for (int li = 0; li < letter.Count; ++li) { GeneralPolygon2d gp = new GeneralPolygon2d(letter[li]); gp.Scale(scale2d, center); gp.Translate(-center); letter[li] = gp; } List <MeshFaceSelection> letter_interiors = new List <MeshFaceSelection>(); bool bSimplify = true; for (int li = 0; li < letter.Count; ++li) { GeneralPolygon2d gp = letter[li]; MeshInsertUVPolyCurve outer = new MeshInsertUVPolyCurve(mesh, gp.Outer); Util.gDevAssert(outer.Validate() == ValidationStatus.Ok); outer.Apply(); if (bSimplify) { outer.Simplify(); } List <MeshInsertUVPolyCurve> holes = new List <MeshInsertUVPolyCurve>(gp.Holes.Count); for (int hi = 0; hi < gp.Holes.Count; ++hi) { MeshInsertUVPolyCurve insert = new MeshInsertUVPolyCurve(mesh, gp.Holes[hi]); Util.gDevAssert(insert.Validate() == ValidationStatus.Ok); insert.Apply(); if (bSimplify) { insert.Simplify(); } holes.Add(insert); } // find a triangle connected to loop that is inside the polygon // [TODO] maybe we could be a bit more robust about this? at least // check if triangle is too degenerate... int seed_tri = -1; EdgeLoop outer_loop = outer.Loops[0]; for (int i = 0; i < outer_loop.EdgeCount; ++i) { if (!mesh.IsEdge(outer_loop.Edges[i])) { continue; } Index2i et = mesh.GetEdgeT(outer_loop.Edges[i]); Vector3d ca = mesh.GetTriCentroid(et.a); bool in_a = gp.Outer.Contains(ca.xy); Vector3d cb = mesh.GetTriCentroid(et.b); bool in_b = gp.Outer.Contains(cb.xy); if (in_a && in_b == false) { seed_tri = et.a; break; } else if (in_b && in_a == false) { seed_tri = et.b; break; } } Util.gDevAssert(seed_tri != -1); // make list of all outer & hole edges HashSet <int> loopEdges = new HashSet <int>(outer_loop.Edges); foreach (var insertion in holes) { foreach (int eid in insertion.Loops[0].Edges) { loopEdges.Add(eid); } } // flood-fill inside loop from seed triangle MeshFaceSelection sel = new MeshFaceSelection(mesh); sel.FloodFill(seed_tri, null, (eid) => { return(loopEdges.Contains(eid) == false); }); letter_interiors.Add(sel); } // extrude regions Func <Vector3d, Vector3f, int, Vector3d> OffsetF = (v, n, i) => { return(v + Vector3d.AxisZ); }; foreach (var interior in letter_interiors) { MeshExtrudeFaces extrude = new MeshExtrudeFaces(mesh, interior); extrude.ExtrudedPositionF = OffsetF; extrude.Extrude(); } TestUtil.WriteTestOutputMesh(mesh, "insert_uv_string.obj"); }
public static void test_uv_insert_segment() { DMesh3 mesh = TestUtil.LoadTestInputMesh("plane_250v.obj"); mesh.EnableVertexUVs(Vector2f.Zero); MeshTransforms.ConvertYUpToZUp(mesh); DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh); spatial.Build(); int tid = spatial.FindNearestTriangle(Vector3d.Zero); //Polygon2d poly = Polygon2d.MakeRectangle(Vector2d.Zero, 5, 5); Polygon2d poly = Polygon2d.MakeCircle(5, 13); //PolyLine2d poly = new PolyLine2d( new Vector2d[] { -5 * Vector2d.One, 5 * Vector2d.One }); //int tri_edge0 = mesh.GetTriEdge(tid, 0); //Index2i edge0_tris = mesh.GetEdgeT(tri_edge0); //Index2i edge0_verts = mesh.GetEdgeV(tri_edge0); //Vector3d v0 = mesh.GetVertex(edge0_verts.a), v1 = mesh.GetVertex(edge0_verts.b); //Vector3d c = mesh.GetTriCentroid(tid); //Polygon2d poly = new Polygon2d(new Vector2d[] { // Vector2d.Lerp(v0.xy, v1.xy, -0.25), // Vector2d.Lerp(v0.xy, v1.xy, 1.5), // c.xy //}); MeshInsertUVPolyCurve insert = new MeshInsertUVPolyCurve(mesh, poly); insert.Apply(); Polygon2d test_poly = new Polygon2d(); List <double> distances = new List <double>(); List <int> nearests = new List <int>(); for (int i = 0; i < insert.Loops[0].VertexCount; ++i) { Vector2d v = mesh.GetVertex(insert.Loops[0].Vertices[i]).xy; test_poly.AppendVertex(v); int iNear; double fNear; distances.Add(poly.DistanceSquared(v, out iNear, out fNear)); nearests.Add(iNear); } System.Console.WriteLine("inserted loop poly has {0} edges", insert.Loops[0].EdgeCount); // find a triangle connected to loop that is inside the polygon // [TODO] maybe we could be a bit more robust about this? at least // check if triangle is too degenerate... int seed_tri = -1; for (int i = 0; i < insert.Loops[0].EdgeCount; ++i) { Index2i et = mesh.GetEdgeT(insert.Loops[0].Edges[i]); Vector3d ca = mesh.GetTriCentroid(et.a); bool in_a = poly.Contains(ca.xy); Vector3d cb = mesh.GetTriCentroid(et.b); bool in_b = poly.Contains(cb.xy); if (in_a && in_b == false) { seed_tri = et.a; break; } else if (in_b && in_a == false) { seed_tri = et.b; break; } } Util.gDevAssert(seed_tri != -1); // flood-fill inside loop HashSet <int> loopEdges = new HashSet <int>(insert.Loops[0].Edges); MeshFaceSelection sel = new MeshFaceSelection(mesh); sel.FloodFill(seed_tri, null, (eid) => { return(loopEdges.Contains(eid) == false); }); // delete inside loop MeshEditor editor = new MeshEditor(mesh); editor.RemoveTriangles(sel, true); MeshTransforms.ConvertZUpToYUp(mesh); TestUtil.WriteTestOutputMesh(mesh, "insert_uv_segment.obj"); //OBJWriter writer = new OBJWriter(); //var s = new System.IO.StreamWriter(Program.TEST_OUTPUT_PATH + "mesh_local_param.obj", false); //List<WriteMesh> wm = new List<WriteMesh>() { new WriteMesh(mesh) }; //WriteOptions opt = new WriteOptions() { // bCombineMeshes = false, bWriteGroups = false, bPerVertexColors = true, bPerVertexUVs = true, // AsciiHeaderFunc = () => { return "mttllib checkerboard.mtl\r\nusemtl checkerboard\r\n"; } //}; //writer.Write(s, wm, opt); //s.Close(); }