/// <summary> /// Find regions of the input meshes that are horizontal, returns binned by Z-value. /// Currently only finds upward-facing regions. /// </summary> protected Dictionary <double, List <PlanarRegion> > FindPlanarZRegions(double minDimension, double dotNormalTol = 0.999) { Dictionary <double, List <PlanarRegion> > Regions = new Dictionary <double, List <PlanarRegion> >(); SpinLock region_lock = new SpinLock(); gParallel.ForEach(Meshes, (sliceMesh) => { if (sliceMesh.options.IsCavity == false) { return; } DMesh3 mesh = sliceMesh.mesh; HashSet <int> planar_tris = new HashSet <int>(); foreach (int tid in mesh.TriangleIndices()) { Vector3d n = mesh.GetTriNormal(tid); double dot = n.Dot(Vector3d.AxisZ); if (dot > dotNormalTol) { planar_tris.Add(tid); } } MeshConnectedComponents regions = new MeshConnectedComponents(mesh); regions.FilterF = planar_tris.Contains; regions.FindConnectedT(); foreach (var c in regions) { AxisAlignedBox3d bounds = MeshMeasurements.BoundsT(mesh, c.Indices); if (bounds.Width > minDimension && bounds.Height > minDimension) { double z = Math.Round(bounds.Center.z, PrecisionDigits); PlanarRegion planar = new PlanarRegion() { Mesh = mesh, Z = z, Triangles = c.Indices }; bool taken = false; region_lock.Enter(ref taken); List <PlanarRegion> zregions; if (Regions.TryGetValue(z, out zregions)) { zregions.Add(planar); } else { zregions = new List <PlanarRegion>() { planar }; Regions[z] = zregions; } region_lock.Exit(); } } }); return(Regions); }
public void RemoveComponent(int tid, bool bInteractive) { if (PreviewMesh.IsTriangle(tid) == false) { DebugUtil.Log("MeshEditorTool.RemoveComponent: invalid tid!"); return; } var tris = MeshConnectedComponents.FindConnectedT(PreviewMesh, tid); do_remove_triangles(tris, bInteractive); }
/// <summary> /// this runs inside SO.SafeMeshRead /// </summary> private object LockedComputeMeshBoundaryData(DMesh3 mesh) { ComponentData data = new ComponentData(); data.timestamp = mesh.ShapeTimestamp; MeshConnectedComponents comp = new MeshConnectedComponents(mesh); comp.FindConnectedT(); if (comp.Count == 1) { return(data); } // [TODO] // - a very common case is a huge mesh with a few floaters. We // should have a way to handle this w/o having to create all // these spatial data structures for the huge mesh! We can // do a quick sort based on bounding boxes, and for very // small submeshes we could use non-fast WN, etc, etc... DSubmesh3Set subMeshes = new DSubmesh3Set(mesh, comp); MeshSpatialSort sort = new MeshSpatialSort(); foreach (var submesh in subMeshes) { sort.AddMesh(submesh.SubMesh, submesh); } sort.Sort(); foreach (var solid in sort.Solids) { foreach (var cavity in solid.Cavities) { data.InteriorMeshes.Add(cavity.Mesh); } if (solid.Outer.InsideOf.Count > 0) { data.InteriorMeshes.Add(solid.Outer.Mesh); } } // reverse orientation so that shading and culling works gParallel.ForEach(data.InteriorMeshes, (m) => { m.ReverseOrientation(true); }); return(data); }
public static List <double> Volumes(MeshGeometry3D mesh) { DMesh3 m3 = MeshGeometryToDMesh(mesh); DMesh3[] mC = MeshConnectedComponents.Separate(m3); List <double> ListVol = new List <double>(); foreach (DMesh3 m in mC) { var triangles = m.TriangleIndices(); Func <int, Vector3d> getVertexF = (a) => { Vector3d V = m.GetVertex(a); return(V); }; Vector2d Vol = MeshMeasurements.VolumeArea(m, triangles, getVertexF); ListVol.Add(Vol.x / 1000); } return(ListVol); }
public static MeshConnectedComponents FindConnectedComponents(CuttingInfo info, List <int> painted) { var components = new MeshConnectedComponents(info.mesh); if (info.data.Multipiece) { components.FilterF = i => info.mesh.GetTriangleGroup(i) == info.data.ColorNum; components.FindConnectedT(); } else { var newC = new MeshConnectedComponents.Component { Indices = painted.ToArray() }; components.Components.Add(newC); } return(components); }
public static void test_sort_mesh_components() { DMesh3 mesh = TestUtil.LoadTestInputMesh("bunny_nested_spheres.obj"); MeshConnectedComponents components = new MeshConnectedComponents(mesh); components.FindConnectedT(); DSubmesh3Set componentMeshes = new DSubmesh3Set(mesh, components); LocalProfiler p = new LocalProfiler(); p.Start("sort"); MeshSpatialSort sorter = new MeshSpatialSort(); foreach (DSubmesh3 submesh in componentMeshes) { sorter.AddMesh(submesh.SubMesh, submesh); } sorter.Sort(); p.Stop("sort"); System.Console.WriteLine(p.AllTimes()); DMesh3 resultMesh = new DMesh3(); foreach (var solid in sorter.Solids) { if (solid.Outer.InsideOf.Count == 0) { MeshEditor.Append(resultMesh, solid.Outer.Mesh); } } TestUtil.WriteTestOutputMesh(resultMesh, "mesh_components.obj"); }
public virtual void Update() { base.begin_update(); int start_timestamp = this.CurrentInputTimestamp; if (MeshSource == null) { throw new Exception("SeparateSolidsOp: must set valid MeshSource to compute!"); } ResultMeshes = null; try { DMesh3 meshIn = new DMesh3(MeshSource.GetDMeshUnsafe()); MeshConnectedComponents comp = new MeshConnectedComponents(meshIn); comp.FindConnectedT(); DSubmesh3Set subMeshes = new DSubmesh3Set(meshIn, comp); List <DMesh3> curMeshes = new List <DMesh3>(); foreach (var submesh in subMeshes) { curMeshes.Add(submesh.SubMesh); } if (group_nested_shells) { MeshSpatialSort sort = new MeshSpatialSort(); foreach (var mesh in curMeshes) { sort.AddMesh(mesh, mesh); } sort.Sort(); curMeshes.Clear(); foreach (var solid in sort.Solids) { DMesh3 outer = solid.Outer.Mesh; if (orient_nested_shells && is_outward_oriented(outer) == false) { outer.ReverseOrientation(); } foreach (var hole in solid.Cavities) { if (orient_nested_shells && hole.Mesh.CachedIsClosed && is_outward_oriented(hole.Mesh) == true) { hole.Mesh.ReverseOrientation(); } MeshEditor.Append(outer, hole.Mesh); } curMeshes.Add(outer); } } ResultMeshes = curMeshes; base.complete_update(); } catch (Exception) { ResultMeshes = new List <DMesh3>(); base.complete_update(); throw; } }
void process() { DMesh3 useSourceMesh = SourceMesh; // try to do simple mesh repairs if (useSourceMesh.CachedIsClosed == false) { useSourceMesh = new DMesh3(SourceMesh); // [TODO] should remove duplicate triangles here? RemoveDuplicateTriangles dupes = new RemoveDuplicateTriangles(useSourceMesh); dupes.Apply(); // close cracks MergeCoincidentEdges merge = new MergeCoincidentEdges(useSourceMesh); //merge.OnlyUniquePairs = true; merge.Apply(); } //Util.WriteDebugMesh(useSourceMesh, "c:\\scratch\\__FIRST_MERGE.obj"); DMesh3[] components = MeshConnectedComponents.Separate(useSourceMesh); List <DMesh3> solidComps = new List <DMesh3>(); foreach (DMesh3 mesh in components) { // [TODO] check if this is a mesh w/ cracks, in which case we // can do other processing? bool closed = mesh.CachedIsClosed; if (closed == false) { OpenMeshes.Add(mesh); continue; } solidComps.Add(mesh); } if (solidComps.Count == 0) { return; } if (solidComps.Count == 1) { ClosedSolids = new List <DMesh3>() { solidComps[0] }; } if (HasNoVoids) { // each solid is a separate solid ClosedSolids = process_solids_novoid(solidComps); } else { ClosedSolids = process_solids(solidComps); } }
public override DMesh3 Cut(CuttingInfo info) { var painted = FindPaintedTriangles(info.mesh, info.data.ColorNum); if (painted.Count <= 0) { return(info.mesh); } var components = new MeshConnectedComponents(info.mesh); components.FilterF = i => info.mesh.GetTriangleGroup(i) == info.data.ColorNum; components.FindConnectedT(); var subMeshes = new List <DMesh3>(); foreach (var component in components) { DSubmesh3 subMesh = new DSubmesh3(info.mesh, component.Indices); var newMesh = subMesh.SubMesh; newMesh.EnableTriangleGroups(); var normals = new List <Vector3d>(); var vertices = new List <Vector3d>(); foreach (var componentTriIndex in component.Indices) { var tri = info.mesh.GetTriangle(componentTriIndex); var normal = info.mesh.GetTriNormal(componentTriIndex); normals.Add(normal); var orgA = info.mesh.GetVertex(tri.a); vertices.Add(orgA); var orgB = info.mesh.GetVertex(tri.b); vertices.Add(orgB); var orgC = info.mesh.GetVertex(tri.c); vertices.Add(orgC); } var avgNormal = normals.Average(); var avgVertices = vertices.Average(); var newPoint = avgVertices - avgNormal * info.data.depth; if (info.data.modifier == CutSettingData.Modifier.Compute) { newPoint = MovePointInsideAndAwayFromShell(info, newPoint); } if (info.data.modifier == CutSettingData.Modifier.DepthDependant) { newPoint = MovePointDepthDependant(info, avgVertices, avgNormal); } component.Indices.ToList().ForEach(index => info.mesh.RemoveTriangle(index)); var newPointId = newMesh.AppendVertex(newPoint); var newPointIdInOldMesh = info.mesh.AppendVertex(newPoint); info.PointToPoint.Add(newPointId, newPointIdInOldMesh); var eidsNewMesh = newMesh.BoundaryEdgeIndices().ToList(); foreach (var openEdge in eidsNewMesh) { var edge = newMesh.GetOrientedBoundaryEdgeV(openEdge); newMesh.AppendTriangle(edge.b, edge.a, newPointId, info.data.ColorNum); info.mesh.AppendTriangle(subMesh.MapVertexToBaseMesh(edge.a), subMesh.MapVertexToBaseMesh(edge.b), newPointIdInOldMesh, ColorManager.Instance.MainColorId); } subMeshes.Add(newMesh); } InstantiateNewObjects(info, subMeshes); return(info.mesh); }
public virtual void Update() { base.begin_update(); int start_timestamp = this.CurrentInputTimestamp; if (MeshSource == null) { throw new Exception("CombineMeshesOp: must set valid MeshSource to compute!"); } ResultMesh = null; try { DMesh3 meshIn = new DMesh3(MeshSource.GetDMeshUnsafe()); if (orient_nested_shells) { MeshConnectedComponents comp = new MeshConnectedComponents(meshIn); comp.FindConnectedT(); DSubmesh3Set subMeshes = new DSubmesh3Set(meshIn, comp); List <DMesh3> curMeshes = new List <DMesh3>(); foreach (var submesh in subMeshes) { curMeshes.Add(submesh.SubMesh); } MeshSpatialSort sort = new MeshSpatialSort(); foreach (var mesh in curMeshes) { sort.AddMesh(mesh, mesh); } sort.Sort(); ResultMesh = new DMesh3(); MeshEditor editor = new MeshEditor(ResultMesh); foreach (var solid in sort.Solids) { DMesh3 outer = solid.Outer.Mesh; if (!is_outward_oriented(outer)) { outer.ReverseOrientation(); } editor.AppendMesh(outer, ResultMesh.AllocateTriangleGroup()); foreach (var hole in solid.Cavities) { if (hole.Mesh.CachedIsClosed && is_outward_oriented(hole.Mesh) == true) { hole.Mesh.ReverseOrientation(); } editor.AppendMesh(hole.Mesh, ResultMesh.AllocateTriangleGroup()); } } } else { ResultMesh = meshIn; } base.complete_update(); } catch (Exception e) { PostOnOperatorException(e); ResultMesh = base.make_failure_output(MeshSource.GetDMeshUnsafe()); base.complete_update(); } }
protected virtual DMesh3 compute_wrap() { DMesh3 meshIn = MeshSource.GetDMeshUnsafe(); double unsigned_offset = Math.Abs(distance); if (cached_sdf == null || unsigned_offset > cached_sdf_max_offset || grid_cell_size != cached_sdf.CellSize) { DMeshAABBTree3 use_spatial = input_spatial; CachingMeshSDF sdf = new CachingMeshSDF(meshIn, grid_cell_size, use_spatial); sdf.MaxOffsetDistance = 2 * (float)unsigned_offset; sdf.CancelF = is_invalidated; sdf.Initialize(); if (is_invalidated()) { return(null); } cached_sdf = sdf; cached_sdf_max_offset = unsigned_offset; cached_sdf_bounds = meshIn.CachedBounds; } var grid_iso = new CachingMeshSDFImplicit(cached_sdf); // currently MCPro-Continuation does not work w/ non-zero // isovalues, so we have to shift our target offset externally var iso = new ImplicitOffset3d() { A = grid_iso, Offset = distance }; MarchingCubesPro c = new MarchingCubesPro(); c.Implicit = iso; c.Bounds = cached_sdf_bounds; c.CubeSize = mesh_cell_size; c.Bounds.Expand(distance + 3 * c.CubeSize); c.CancelF = is_invalidated; c.GenerateContinuation(offset_seeds(meshIn, distance)); if (is_invalidated()) { return(null); } Reducer r = new Reducer(c.Mesh); r.FastCollapsePass(c.CubeSize * 0.5, 3, true); if (is_invalidated()) { return(null); } if (min_component_volume > 0) { MeshEditor.RemoveSmallComponents(c.Mesh, min_component_volume, min_component_volume); } if (is_invalidated()) { return(null); } DMesh3 offsetMesh = c.Mesh; MeshConnectedComponents comp = new MeshConnectedComponents(offsetMesh); comp.FindConnectedT(); if (is_invalidated()) { return(null); } DSubmesh3Set subMeshes = new DSubmesh3Set(offsetMesh, comp); if (is_invalidated()) { return(null); } MeshSpatialSort sort = new MeshSpatialSort(); foreach (var subMesh in subMeshes) { sort.AddMesh(subMesh.SubMesh, subMesh); } sort.Sort(); if (is_invalidated()) { return(null); } DMesh3 outerMesh = new DMesh3(); foreach (var solid in sort.Solids) { DMesh3 outer = solid.Outer.Mesh; //if (is_outward_oriented(outer) == false) // outer.ReverseOrientation(); MeshEditor.Append(outerMesh, outer); } if (is_invalidated()) { return(null); } return(compute_inset(outerMesh)); }
protected void do_flatten(DMesh3 mesh) { double BAND_HEIGHT = flatten_band_height; Vector3d down_axis = -Vector3d.AxisY; double dot_thresh = 0.2; AxisAlignedBox3d bounds = mesh.CachedBounds; DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh, true); Ray3d ray = new Ray3d(bounds.Center - 2 * bounds.Height * Vector3d.AxisY, Vector3d.AxisY); int hit_tid = spatial.FindNearestHitTriangle(ray); Frame3f hitF; MeshQueries.RayHitPointFrame(mesh, spatial, ray, out hitF); Vector3d basePt = hitF.Origin; Frame3f basePlane = new Frame3f(basePt, Vector3f.AxisY); MeshConnectedComponents components = new MeshConnectedComponents(mesh) { FilterF = (tid) => { if (mesh.GetTriangleGroup(tid) != LastExtrudeOuterGroupID) { return(false); } Vector3d n, c; double a; mesh.GetTriInfo(tid, out n, out a, out c); double h = Math.Abs(c.y - basePt.y); if (h > BAND_HEIGHT) { return(false); } if (n.Dot(down_axis) < dot_thresh) { return(false); } return(true); }, SeedFilterF = (tid) => { return(tid == hit_tid); } }; components.FindConnectedT(); MeshFaceSelection all_faces = new MeshFaceSelection(mesh); foreach (var comp in components) { MeshVertexSelection vertices = new MeshVertexSelection(mesh); vertices.SelectTriangleVertices(comp.Indices); foreach (int vid in vertices) { Vector3d v = mesh.GetVertex(vid); v = basePlane.ProjectToPlane((Vector3f)v, 2); mesh.SetVertex(vid, v); } all_faces.SelectVertexOneRings(vertices); } all_faces.ExpandToOneRingNeighbours(3, (tid) => { return(mesh.GetTriangleGroup(tid) == LastExtrudeOuterGroupID); }); RegionRemesher r = new RegionRemesher(mesh, all_faces); r.SetProjectionTarget(MeshProjectionTarget.Auto(mesh)); r.SetTargetEdgeLength(2.0f); r.SmoothSpeedT = 1.0f; for (int k = 0; k < 10; ++k) { r.BasicRemeshPass(); } r.SetProjectionTarget(null); r.SmoothSpeedT = 1.0f; for (int k = 0; k < 10; ++k) { r.BasicRemeshPass(); } r.BackPropropagate(); }