Shrinkwrap issues

I have some issues with shrinkwrapping files which are going to be 3D printed.

In my grasshopper definition I’m using Isopod and then shrinkwrap to lower the mesh density and a bit of smoothing. The mesh output is closed but some of the faces are basically overlapping.

Here’s a detail and the file is below

2026-09-29_shrinkwrap-issue.3dm (2.8 MB)

I’ll try to create a simplified example.

Curious, have you tried sending them to a printer?

A lot of slicing software will simply ignore this type of issue as long as the part is closed.

Yes I had numerous files like this printed successfully.

Does the same file do this in Rhino 8?

Ok here’s a file which replicates the problem.

The problematic mesh faces can be seen best when the mesh is baked and then Concept mode shows sharp creases. I added points on four edges.

The example in the first post has worse overlaps than this:

2026-09-29_shrinkwrap-issue_02.gh (104.0 KB)

2026-09-29_shrinkwrap-issue_02.3dm (1.0 MB)

This is a detail of the shared grasshopper definition in Rhino 8. It shows the same issue with some of the triangles being very narrow.

It seems like shrinkwrap should check the aspect ratio and insert an edge.

Here’s a nasty one.

Maybe I just need to triangulate and run collapse by aspect ratio?

@Trav or @theoutside what’s the quickest way to find those faces?

Mesh has 1 face where the face normal differs substantially from the vertex normals.

To find them.. i dont think theres an existing command but a script can locate them pretty quickly.

However, the RebuildMeshNormals command should probably fix them without having to hassle with them.

import Rhino, scriptcontext as sc
from Rhino.Geometry import ComponentIndex, ComponentIndexType, Vector3f

rc, ref = Rhino.Input.RhinoGet.GetOneObject("Mesh", False, Rhino.DocObjects.ObjectType.Mesh)
if rc == Rhino.Commands.Result.Success:
    obj = ref.Object()
    m = ref.Mesh().DuplicateMesh()
    m.FaceNormals.ComputeFaceNormals()
    sc.doc.Objects.UnselectAll()
    hits = []
    for i in range(m.Faces.Count):
        f = m.Faces[i]
        vis = [f.A, f.B, f.C, f.D] if f.IsQuad else [f.A, f.B, f.C]
        avg = Vector3f(0, 0, 0)
        for vi in vis:
            avg += m.Normals[vi]
        avg.Unitize()
        fn = m.FaceNormals[i]
        fn.Unitize()
        if avg * fn < 0:
            hits.append(i)
            obj.SelectSubObject(ComponentIndex(ComponentIndexType.MeshFace, i), True, True)
    sc.doc.Views.Redraw()
    print("Bad faces: {}".format(hits))

Ok so one way to get rid of the narrow faces is by running CollapseMeshFacesByAspectRatio with a certain threshold until no faces hit the threshold.

Thanks @Trav that helps.

And I clauded something to eliminate the slivers in Grasshopper.

using System;
using System.Collections.Generic;
using System.Linq;
using Rhino.Geometry;

public class Script_Instance : GH_ScriptInstance

{

  private void RunScript(Mesh M, double MaxAR, int MaxIter, ref object OutMesh, ref object Log)

  {

    if (M == null) return;
    if (MaxAR <= 1.0) MaxAR = 25.0;
    if (MaxIter <= 0) MaxIter = 100;

    Mesh work = M.DuplicateMesh();
    work.Faces.ConvertQuadsToTriangles();
    work.Vertices.CombineIdentical(true, true);
    work.Vertices.CullUnused();

    int startFaces = work.Faces.Count;
    int totalCollapsed = 0;
    int pass = 0;

    for (pass = 0; pass < MaxIter; pass++)

    {

      int n = work.CollapseFacesByByAspectRatio(MaxAR);   // modifies 'work' in place
      if (n == 0) break;
      totalCollapsed += n;

      // tidy up so the next pass sees a clean mesh
      work.Faces.CullDegenerateFaces();
      work.Vertices.CullUnused();
      work.Compact();

    }

    work.Faces.CullDegenerateFaces();
    work.Vertices.CullUnused();
    work.Normals.ComputeNormals();
    work.Compact();

    // Report what is left, using Rhino's own aspect ratio measure

    int remaining = 0;
    double worst = 0;

    for (int i = 0; i < work.Faces.Count; i++)

    {

      double ar = work.Faces.GetFaceAspectRatio(i);
      if (ar > MaxAR) remaining++;
      if (ar > worst) worst = ar;

    }




    OutMesh = work;

    Log = string.Format(

      "Passes: {0} | Collapsed: {1} | Faces: {2} -> {3} | Faces still > {4}: {5} | Worst AR: {6:0.##}",

      pass + 1, totalCollapsed, startFaces, work.Faces.Count, MaxAR, remaining, worst);

  }


}