Evalutate Surface With Code's surface normal is not the "same" direction with Grasshopper's "Evalutate Surface" Component

The Evalutate surface with C# Code, And the normal of the surface is not the “same” with When I using Grasshopper “Evalutate Surface”, The C# Code is below:

double U = Surface.Domain(0).Mid;
double V = Surface.Domain(1).Mid;
Vector3d Vec = Surface.NormalAt(U, V);
Point3d _Pt = Surface.PointAt(U, V);
Vector = Vec;
Point = _Pt;

The picture show evalutate surface with component:

The picture show evalutate surface with compnent and Code and the green vector is the result evaluate surface with Component, red is the result evalutate with C# code;

Is anyone can help me? Thanks

Check your c# surface domain, it’s sim that your uvs are not split in u or v ,in your gh definition the normal is computed from a closest point not a domain division like in your c# def so the difference is normal

(barycentre computation from boundary are sometimes different than surface domain *.5)

How Can I knew wether the uvs are not split in u or v? and I think for the surface the two method will get the same direction for the surface normal, However is not

Hi @cughudson,

Since your input is a Brep, you sould be evaluating the BrepFace, not the underlying surface.

– Dale

@dale OK Thanks, It is helpful

Hi @dale, The underlying surface is so difficult to understand for me, could you show me what is underlying surface excatcly is? Thanks

Breps are made of multiple trimmed or untrimmed surfaces called BrepFaces, each of which have their own independent u-v domains.

Area centroid is generally not the same thing as the middle of the surface domain - To obtain the same result as the GH definition you will have to use the AreaMassProperties class: http://developer.rhino3d.com/api/RhinoCommon/html/T_Rhino_Geometry_AreaMassProperties.htm

and this very cumbersome overload of Brep.ClosestPoint():
http://developer.rhino3d.com/api/RhinoCommon/html/M_Rhino_Geometry_Brep_ClosestPoint_1.htm

Hi @cughudson,

This diagram can be helpful:

http://developer.rhino3d.com/guides/cpp/brep-data-structure/

@qythium

A Brep Face is just a proxy to the underlying surface. A face get its parameterization, including domain, from the surface.

– Dale

1 Like

@dale, Thankyou, I have understand the Brep through the diagram, Thankyou very much

Hi @dale,

In my case, the input is a surface, but I still get the flipped normal direction using C#.

    S.SetDomain(0, new Interval(0.0, 1));
    S.SetDomain(1, new Interval(0.0, 1));

    Point3d cp = S.PointAt(0.5, 0.5);
    Vector3d nv = S.NormalAt(0.5, 0.5);

    centerPt = cp;
    normal = nv;

[normal.gh|attachment]

(upload://zrUZsMpS1egi2JMLZPep4xpDdTV.gh) (8.5 KB)
Can I know the reason behind this behavior? I’m attaching this example file.
Thank you in advance.

Dongyeopnormal.gh (8.5 KB)

Hi @Dongyeop_Lee,

By just evaluating the Surface, you don’t taking the referencing BrepFace into account.

Try this: bake your surface to Rhino, select the surface, and run the List command. You’ll see this:

image

As you can see, by the reverse(1), the face orientation is opposite of natural surface orientation.

Also, it’s best not to reparametrize curves or surfaces if you don’t have to. The Interval class contains methods convert to normalized parameters from interval parameters, and vise versa.

When curves and surfaces are parameterized with a [0,1] domain, both the accuracy and the precision of geometric calculations like intersections and closest points are reduced, sometimes dramatically.

There is a better approach to evaluating a surface at the center:

private void RunScript(Surface S, ref object centerPt, ref object normal)
{
  Interval domain_u = S.Domain(0);
  Interval domain_v = S.Domain(1);

  double norm_u = domain_u.ParameterAt(0.5);
  double norm_v = domain_v.ParameterAt(0.5);

  Point3d cp = S.PointAt(norm_u, norm_v);
  Vector3d nv = S.NormalAt(norm_u, norm_v);

  centerPt = cp;
  normal = nv;
}

Here is a re-factored version of your code that works more like Grasshopper’s component:

private void RunScript(Brep B, ref object centerPt, ref object normal)
{
  if (null != B && 1 == B.Faces.Count)
  {
    BrepFace F = B.Faces[0];

    Interval domain_u = F.Domain(0);
    Interval domain_v = F.Domain(1);

    double norm_u = domain_u.ParameterAt(0.5);
    double norm_v = domain_v.ParameterAt(0.5);

    Point3d cp = F.PointAt(norm_u, norm_v);
    Vector3d nv = F.NormalAt(norm_u, norm_v);

    if (F.OrientationIsReversed)
      nv.Reverse();

    centerPt = cp;
    normal = nv;
  }
}

Hope this helps.

– Dale

Thank you very much for the detailed explanation! It’s very helpful.
It seems much better to use Brep as input instead of Surface in general, because Surface also doesn’t allow to get accurate edge curves as I asked in this post. Surface boundary lines c#