code: def vertex_normals(
      vertices: type_alias.TensorLike,
      indices: type_alias.TensorLike,
      clockwise: bool = True,
      name: str = "normals_vertex_normals",
  ) -> type_alias.TensorLike:
      """Computes vertex normals from a mesh.
  
      This function computes vertex normals as the weighted sum of the adjacent
      face normals, where the weights correspond to the area of each face. This
      function supports planar convex polygon faces. For non-triangular meshes,
      this function converts them into triangular meshes to calculate vertex
      normals.
  
      Note:
        In the following, A1 to An are optional batch dimensions.
  
      Args:
        vertices: A tensor of shape `[A1, ..., An, V, 3]`, where V is the number of
          vertices.
        indices: A tensor of shape `[A1, ..., An, F, M]`, where F is the number of
          faces and M is the number of vertices per face.
        clockwise: Winding order to determine front-facing faces. The order of
          vertices should be either clockwise or counterclockwise.
        name: A name for this op. Defaults to "normals_vertex_normals".
  
      Returns:
        A tensor of shape `[A1, ..., An, V, 3]` containing vertex normals. If
        vertices and indices have different batch dimensions, this function
        broadcasts them into the same batch dimensions and the output batch
        dimensions are the broadcasted.
  
      Raises:
        ValueError: If the shape of `vertices`, `indices` is not supported.
      """
      with tf.name_scope(name):
          vertices = tf.convert_to_tensor(value=vertices)
          indices = tf.convert_to_tensor(value=indices)
  
          shape.check_static(
              tensor=vertices,
              tensor_name="vertices",
              has_rank_greater_than=1,
              has_dim_equals=(-1, 3),
          )
          shape.check_static(
              tensor=indices,
              tensor_name="indices",
              has_rank_greater_than=1,
              has_dim_greater_than=(-1, 2),
          )
          shape.compare_batch_dimensions(
              tensors=(vertices, indices),
              last_axes=(-3, -3),
              broadcast_compatible=True,
          )
  
          shape_indices = indices.shape.as_list()
          if None in shape_indices[:-2]:
              raise ValueError(
                  "'indices' must have specified batch dimensions."
              )
          common_batch_dims = shape.get_broadcasted_shape(
              vertices.shape[:-2], indices.shape[:-2]
          )
          vertices_repeat = [
              common_batch_dims[x] // vertices.shape.as_list()[x]
              for x in range(len(common_batch_dims))
          ]
          indices_repeat = [
              common_batch_dims[x] // shape_indices[x]
              for x in range(len(common_batch_dims))
          ]
          vertices = tf.tile(
              vertices,
              vertices_repeat + [1, 1],
              name="vertices_broadcast",
          )
          indices = tf.tile(
              indices, indices_repeat + [1, 1], name="indices_broadcast"
          )
  
          # Triangulate non-triangular faces.
          if shape_indices[-1] > 3:
              triangle_indices = []
              for i in range(1, shape_indices[-1] - 1):
                  triangle_indices.append(
                      tf.concat(
                          (indices[..., 0:1], indices[..., i : i + 2]),
                          axis=-1,
                      )
                  )
              indices = tf.concat(triangle_indices, axis=-2)
              shape_indices = indices.shape.as_list()
  
          face_vertices = gather_faces(vertices, indices)
          # Use unnormalized face normals to scale normals by area.
          mesh_face_normals = face_normals(
              face_vertices, clockwise=clockwise, normalize=False
          )
  
          if vertices.shape.ndims > 2:
              outer_indices = np.meshgrid(
                  *[np.arange(i) for i in shape_indices[:-2]],
                  sparse=False,
                  indexing="ij",
              )
              outer_indices = [
                  np.expand_dims(i, axis=-1) for i in outer_indices
              ]
              outer_indices = np.concatenate(outer_indices, axis=-1)
              outer_indices = np.expand_dims(outer_indices, axis=-2)
              outer_indices = tf.constant(outer_indices, dtype=tf.int32)
              outer_indices = tf.tile(
                  outer_indices,
                  [1] * len(shape_indices[:-2])
                  + [tf.shape(input=indices)[-2]]
                  + [1],
              )
              unnormalized_vertex_normals = tf.zeros_like(vertices)
              for i in range(shape_indices[-1]):
                  scatter_indices = tf.concat(
                      [outer_indices, indices[..., i : i + 1]], axis=-1
                  )
                  unnormalized_vertex_normals = (
                      tf.tensor_scatter_nd_add(
                          unnormalized_vertex_normals,
                          scatter_indices,
                          mesh_face_normals,
                      )
                  )
          else:
              unnormalized_vertex_normals = tf.zeros_like(vertices)
              for i in range(shape_indices[-1]):
                  unnormalized_vertex_normals = (
                      tf.tensor_scatter_nd_add(
                          unnormalized_vertex_normals,
                          indices[..., i : i + 1],
                          mesh_face_normals,
                      )
                  )
  
          vector_norms = tf.sqrt(
              tf.reduce_sum(
                  input_tensor=unnormalized_vertex_normals**2,
                  axis=-1,
                  keepdims=True,
              )
          )
          return safe_ops.safe_unsigned_div(
              unnormalized_vertex_normals, vector_norms
          )
  
file_path: graphics/tensorflow_graphics/geometry/representation/mesh/normals.py
imports: from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  import numpy
  from six.moves import range
  import tensorflow
  from tensorflow_graphics.geometry.representation import triangle
  from tensorflow_graphics.util import export_api
  from tensorflow_graphics.util import safe_ops
  from tensorflow_graphics.util import shape
  from tensorflow_graphics.util import type_alias
label_desc: Use image node_id 3 for calling a global function with example usage: vertex_normals(vertices, indices, clockwise, name) and returns: safe_ops