5

I need to find a normal pointing outside for every face / cell of a closed Mesh Region.

How can I do this?

Here's what I have now - mesh + mesh cells centers and I'm stuck and puzzled. There's VertexNormals, but there's no "CellNormals" or whatever else.

(* import OBJ *)
input = Import["torus.obj"];

(* Faces & Points ) coords = MeshCoordinates[input]; ( вершины ) cells = MeshCells[input, 2]; ( грани *)

(* Boundary Mesh Region *) torus = BoundaryMeshRegion[coords, cells, MeshCellStyle -> Opacity[0.3]];

(* Cell Centers *) meshprimitives = MeshPrimitives[torus, 2]; cellcenters = Table[RegionCentroid[meshprimitives[[i]]], {i, meshprimitives // Length}]; ссPoints = ListPointPlot3D[cellcenters, PlotStyle -> Red];

(* Graph *) Show[torus, ссPoints]

Here's what I need:

enter image description here

NB! The normals need to be pointed outside.

Please help!

J. M.'s missing motivation
  • 124,525
  • 11
  • 401
  • 574
Anton
  • 345
  • 1
  • 9

2 Answers2

8

The conventional way to get face normals is to use Newell's method (which I have also used in previous answers):

newellNormals[pts_?MatrixQ] := Module[{tp = Transpose[pts]}, Normalize[MapThread[Dot,
              {RotateLeft[ListConvolve[{{-1, 1}}, tp, {-1, -1}]], 
               RotateRight[ListConvolve[{{1, 1}}, tp, {-1, -1}]]}]]]

This assumes that all the polygons in your mesh are consistently oriented: if a polygon in your mesh is oriented anticlockwise from where it is being viewed, then the resulting normal will point in that direction. (Both FindMeshDefects[] and to a lesser extent FaceForm[] are useful for diagnostics like this.)

Since the OP does not give a usable example, I'll use one of the built-in meshes:

herbie = ExampleData[{"Geometry3D", "UtahVWBug"}, "MeshRegion"];
polys = MeshPrimitives[herbie, 2];
nrms = newellNormals @@@ polys;
Graphics3D[{{Arrowheads[Tiny], GrayLevel[0.25], 
             MapThread[With[{c = Mean @@ #1},
                            Arrow[Tube[{c, c + 5 #2}]]] &,
                       {polys, nrms}]},
            {Directive[Hue[0.6, 0.3, 0.85], EdgeForm[], 
                       FaceForm[Specularity[0.2, 30]]], polys}}, 
           Lighting -> {{"Ambient", GrayLevel[0.45]},
                        {"Directional", GrayLevel[0.3], ImageScaled[{2, 0, 2}]},
                        {"Directional", GrayLevel[0.33], ImageScaled[{2, 2, 2}]},
                        {"Directional", GrayLevel[0.3], ImageScaled[{0, 2, 2}]}}]

A Bug's Normals

J. M.'s missing motivation
  • 124,525
  • 11
  • 401
  • 574
8

Here is an approach using the undocumented Region`Mesh`MeshCellNormals function mentioned previously in this answer, from which the vector plotting code I show below is also inspired.

First let's get a coarsely-discretized torus:

input = 
 DiscretizeRegion[
   ParametricRegion[
     {Cos[t] (3 + Cos[u]), Sin[t] (3 + Cos[u]), Sin[u]}, 
     {{t, 0, 2 Pi}, {u, 0, 2 Pi}}
   ],
 MaxCellMeasure -> 1000
]

3D torus, meshed

Then let's use the undocumented fucntion to calculate the direction of the normals to each face:

normals = Region`Mesh`MeshCellNormals[input, 2]

(* Out: {{0.999555, 0.00942983, 0.0282893}, {-0.652107, 0.577115, -0.491625}, ... , {-0.847775, 0.493059, -0.195371}, {0.846814, -0.513404, 0.139001}} *)

... and plot them on the surface, as arrows:

Show[
 input,
 Graphics3D[{
   Thickness[0.002], Arrowheads[Medium],
   {Darker[rc = RandomColor[]], FaceForm[rc], #} & /@
    MapThread[
     {#1, Arrow[{Mean@#1[[1]], Mean@#1[[1]] + Normalize@#2}]} &,
     {MeshPrimitives[input, 2], normals}
     ]
   }]
 ]

3D torus with cell normals plotted as arrows

MarcoB
  • 67,153
  • 18
  • 91
  • 189