How to calculate ngon face normal correctly?

Hi,

How mesh faces normals are normally calculated?

I want to get ngon face normal.

I used method below. But I see it does work only for one polyline, but not on full mesh.
To get a plane origin I sum up all boundary vertices and divided by number of them.
Then for normal vector I thought I could do the same by summing up all vertex normals and dividing by number of them.
But this does not produce normal perpendicular planar ngon face.

Before I was using this method:

    public static Plane[] GetNgonPlanes(this Mesh mesh)
    {
        Plane[] p = new Plane[mesh.Ngons.Count];
        uint[][] b = mesh.GetNGonsBoundaries(); //extension method

        for (int i = 0; i < mesh.Ngons.Count; i++)
        {
            Vector3f temp = new Vector3f();
            for (int j = 0; j < b[i].Length; j++)
                temp += mesh.Normals[(int)b[i][j]];

            if (b[i].Length > 0)
                temp /= b[i].Length;

            p[i] = new Plane(mesh.Ngons.GetNgonCenter(i), temp); //Extension method
        }

        return p;
    }

Ok it has to be cross product:

    public static Vector3d GetNGonNormal(this Mesh mesh, uint[] boundaryVertices)
    {
        //PolyFace item = this[index];
        Vector3d vector3d = new Vector3d();
        int count = checked(boundaryVertices.Length - 1);
        for (int i = 0; i <= count; i++)
        {
            int num = (int)boundaryVertices[(checked(checked(i - 1) + boundaryVertices.Length)) %  boundaryVertices.Length];
            int item1 = (int)boundaryVertices[(checked(checked(i + 1) + boundaryVertices.Length)) % boundaryVertices.Length];
            Point3d point3d = mesh.Vertices[num]; 
            Point3d point3d1 = mesh.Vertices[item1]; 
            Point3d item2 = mesh.Vertices[(int)boundaryVertices[i]];
            vector3d = vector3d + Vector3d.CrossProduct(new Vector3d(item2 - point3d), new Vector3d(point3d1 - item2));
        }

        if (vector3d.X == 0 & vector3d.Y == 0 & vector3d.Z == 0)
            vector3d.Unitize();

        return vector3d;
    }

Hi @Petras_Vestartas,

Rhino will calculate mesh face normals for, by way of MeshFaceNormalList.ComputeFaceNormals, which basically does something like this:

public static Vector3d CalculateMeshFaceNormal(Mesh mesh, int faceIndex)
{
  var n = Vector3d.Unset;
  if (null != mesh && faceIndex >= 0 && faceIndex < mesh.Faces.Count)
  {
    var face = mesh.Faces[faceIndex];
    var a = mesh.Vertices[face.C] - mesh.Vertices[face.A];
    var b = mesh.Vertices[face.D] - mesh.Vertices[face.B];
    n = Vector3d.CrossProduct(a, b);
    n.Unitize();
  }
  return n;
}

Since Rhino’s ngons are planar, if face normals have already been calculated then just get the normal of one of the ngon’s faces.

– Dale

Oh that is more straight forward thank you:)

Would it work also for triangles polylines if this would look like this?

    public static Vector3d CalculateMeshFaceNormal(this Polyline p)
    {
        var n = Vector3d.Unset;
        if (null != p && p.Count-1 > 3)
        {
            var a = p[1] - p[0];
            var b = p[2] - p[1];
            n = Vector3d.CrossProduct(a, b);
            n.Unitize();
        }
        return n;
    }

Here is a more generalized approach:

public static Vector3d CalculatePolygonNormal(Polyline polyline)
{
  var n = Vector3d.Unset;
  if (null != polyline && polyline.IsValid && polyline.IsClosed)
  {
    Plane plane;
    var rc = Plane.FitPlaneToPoints(polyline, out plane);
    if (rc == PlaneFitResult.Success)
      n = plane.Normal;
  }
  return n;
}

– Dale

Does Plane.FitPlaneToPoints take into account direction of polyline? For clockwise pointing in one direction and for anticlockwise pointing into opposite?

It seems that it follow, but it when I rotate polyline in 3d plane z axis is flipping from time to time. While cross product seems to be stable.

This is probably better than my previous example…

public static Vector3d GetPolylineCurveNormal(PolylineCurve curve, double tolerance)
{
  var n = Vector3d.Unset;
  if (null != curve && curve.IsValid && curve.IsClosed)
  {
    if (tolerance < RhinoMath.ZeroTolerance)
      tolerance = RhinoMath.ZeroTolerance;
    Plane plane;
    if (curve.TryGetPlane(out plane, tolerance))
      n = plane.Normal;
  }
  return n;
}

– D