Silhouette.ComputeDraftCurve results not completely on BrepFace

Sometimes Silhouette.ComputeDraftCurve creates curves that deviate from a BrepFace more than the tolerance argument. For example, the 45° draft curve created on the face in
Silhouette_ComputeDraftCurve_dist_error.3dm (38.0 KB)
with tolerance=0.001, can be pulled to the same face about 0.055.

This occurs in both V7 and V8.2, and also via _DraftAngleAnalysis.

Here is a Python script I've been using to study this.
from __future__ import absolute_import, division, print_function, unicode_literals

import Rhino
import Rhino.DocObjects as rd
import Rhino.Geometry as rg
import scriptcontext as sc


draftAngle = Rhino.RhinoMath.ToRadians(45.0)
tolerance = sc.doc.ModelAbsoluteTolerance


def doesCurveSelfIntersect(rgC, tolerance=None):
    if tolerance is None: tolerance = sc.doc.ModelAbsoluteTolerance
    rc = rg.Intersect.Intersection.CurveSelf(rgC, tolerance)
    return bool(rc)


def create_hi_def_pull_to_face(face, rgC_on_face, tolerance=1e-6):

    pulled = rgC_on_face.PullToBrepFace(face=face, tolerance=tolerance)
    if len(pulled) == 1:
        return pulled[0]

    pullback = face.Pullback(rgC_on_face, tolerance=tolerance)
    pushup = face.Pushup(pullback, tolerance=tolerance)
    return pushup


def main():
    
    res, objrefs = Rhino.Input.RhinoGet.GetMultipleObjects(
        prompt="Select breps",
        acceptNothing=False,
        filter=rd.ObjectType.Brep)
    
    if res != Rhino.Commands.Result.Success: return
    
    attr_Red = rd.ObjectAttributes()
    attr_Red.LayerIndex = sc.doc.Layers.CurrentLayerIndex
    attr_Red.ColorSource = rd.ObjectColorSource.ColorFromObject
    attr_Red.ObjectColor = attr_Red.ObjectColor.Red
    
    attr_Green = attr_Red.Duplicate()
    attr_Green.ObjectColor = attr_Green.ObjectColor.Lime
    
    Rhino.RhinoApp.SetCommandPrompt("Working ...")
    
    print("Tolerance: {}".format(tolerance))
    
    for objref in objrefs:
        face_In = objref.Face()
        if face_In is None:
            brep = objref.Brep()
            faces = brep.Faces
        else:
            faces = [face_In]
        
        for face in faces:
            srf = face.UnderlyingSurface()

            ss = []

            silhouettes = rg.Silhouette.ComputeDraftCurve(
                geometry=face,
                draftAngle=draftAngle,
                pullDirection=rg.Vector3d.ZAxis,
                tolerance=tolerance,
                angleToleranceRadians=sc.doc.ModelAngleToleranceRadians)

            for silh in silhouettes:
                if silh.Curve is None: continue
                if silh.SilhouetteType not in (rg.SilhouetteType.DraftCurve, rg.SilhouetteType.Tangent):
                    continue

                fLength = silh.Curve.GetLength()
                if fLength < tolerance:
                    continue

                if doesCurveSelfIntersect(silh.Curve, tolerance):
                    continue

                hi_def_pull = create_hi_def_pull_to_face(face, silh.Curve)
                if doesCurveSelfIntersect(hi_def_pull):
                    continue

                bSuccess, fDistMax = rg.Curve.GetDistancesBetweenCurves(
                    silh.Curve, hi_def_pull, tolerance=0.1*tolerance)[:2]
                if not bSuccess:
                    continue

                ss.append("    Max. dist. from face: {}".format(fDistMax))

                if fDistMax > tolerance:
                    ss[-1] += " <-"
                    sc.doc.Objects.AddCurve(silh.Curve, attr_Red)
                    sc.doc.Objects.AddCurve(hi_def_pull, attr_Green)
            if ss: print('\n'.join(ss))

    sc.doc.Views.Redraw()


if __name__ == '__main__': main()

My workaround in another script is to check whether the curve is within tolerance on the face, and pull it to the face when it isn’t. I have not checked whether the curve is pulled to the target draftAngle.

Hi @spb,

I’ve logged an issue so a developer can look into this.

https://mcneel.myjetbrains.com/youtrack/issue/RH-79657

– Dale