code: def _save_ply(
      f,
      *,
      verts: torch.Tensor,
      faces: Optional[torch.LongTensor],
      verts_normals: Optional[torch.Tensor],
      verts_colors: Optional[torch.Tensor],
      ascii: bool,
      decimal_places: Optional[int] = None,
      colors_as_uint8: bool,
  ) -> None:
      """
      Internal implementation for saving 3D data to a .ply file.
  
      Args:
          f: File object to which the 3D data should be written.
          verts: FloatTensor of shape (V, 3) giving vertex coordinates.
          faces: LongTensor of shape (F, 3) giving faces.
          verts_normals: FloatTensor of shape (V, 3) giving vertex normals.
          verts_colors: FloatTensor of shape (V, 3) giving vertex colors.
          ascii: (bool) whether to use the ascii ply format.
          decimal_places: Number of decimal places for saving if ascii=True.
          colors_as_uint8: Whether to save colors as numbers in the range
                      [0, 255] instead of float32.
      """
      _write_ply_header(
          f,
          verts=verts,
          faces=faces,
          verts_normals=verts_normals,
          verts_colors=verts_colors,
          ascii=ascii,
          colors_as_uint8=colors_as_uint8,
      )
  
      if not (len(verts)):
          warnings.warn("Empty 'verts' provided")
          return
  
      color_np_type = np.ubyte if colors_as_uint8 else np.float32
      verts_dtype = [("verts", np.float32, 3)]
      if verts_normals is not None:
          verts_dtype.append(("normals", np.float32, 3))
      if verts_colors is not None:
          verts_dtype.append(("colors", color_np_type, 3))
  
      vert_data = np.zeros(verts.shape[0], dtype=verts_dtype)
      vert_data["verts"] = verts.detach().cpu().numpy()
      if verts_normals is not None:
          vert_data["normals"] = verts_normals.detach().cpu().numpy()
      if verts_colors is not None:
          color_data = verts_colors.detach().cpu().numpy()
          if colors_as_uint8:
              vert_data["colors"] = np.rint(color_data * 255)
          else:
              vert_data["colors"] = color_data
  
      if ascii:
          if decimal_places is None:
              float_str = b"%f"
          else:
              float_str = b"%" + b".%df" % decimal_places
          float_group_str = (float_str + b" ") * 3
          formats = [float_group_str]
          if verts_normals is not None:
              formats.append(float_group_str)
          if verts_colors is not None:
              formats.append(
                  b"%d %d %d " if colors_as_uint8 else float_group_str
              )
          formats[-1] = formats[-1][:-1] + b"\n"
          for line_data in vert_data:
              for data, format in zip(line_data, formats):
                  f.write(format % tuple(data))
      else:
          if isinstance(f, BytesIO):
              # tofile only works with real files, but is faster than this.
              f.write(vert_data.tobytes())
          else:
              vert_data.tofile(f)
  
      if faces is not None:
          faces_array = faces.detach().cpu().numpy()
  
          _check_faces_indices(faces, max_index=verts.shape[0])
  
          if len(faces_array):
              if ascii:
                  np.savetxt(f, faces_array, "3 %d %d %d")
              else:
                  faces_recs = np.zeros(
                      len(faces_array),
                      dtype=[
                          ("count", np.uint8),
                          ("vertex_indices", np.uint32, 3),
                      ],
                  )
                  faces_recs["count"] = 3
                  faces_recs["vertex_indices"] = faces_array
                  faces_uints = faces_recs.view(np.uint8)
  
                  if isinstance(f, BytesIO):
                      f.write(faces_uints.tobytes())
                  else:
                      faces_uints.tofile(f)
  
imports: import itertools
  import os
  import struct
  import sys
  import warnings
  from collections import namedtuple
  from dataclasses import asdict, dataclass
  from io import BytesIO, TextIOBase
  from typing import List, Optional, Tuple
  import numpy
  import torch
  from iopath.common.file_io import PathManager
  from pytorch3d.io.utils import _check_faces_indices, _make_tensor, _open_file, _read_image, PathOrStr
  from pytorch3d.renderer import TexturesUV, TexturesVertex
  from pytorch3d.structures import Meshes, Pointclouds
  from .pluggable_formats import endswith, MeshFormatInterpreter, PointcloudFormatInterpreter
label_desc: Use image node_id 18 for calling a global function with example usage: _save_ply(f) without return types