class_docstr: A TriMesh represents a mesh of triangles represented as the
  simplices and nodes. The simplices represent a indices into the
  nodes array. The mesh therefore follows a datastructure very
  similar to a graph, with the abstract connectivity between nodes
  stored on the TriMesh element itself, the node positions stored on
  a Nodes element and the concrete paths making up each triangle
  generated when required by accessing the edgepaths.
  
  Unlike a Graph each simplex is represented as the node indices of
  the three corners of each triangle.
class_docstr_tok: ["A","TriMesh","represents","a","mesh","of","triangles","represented","as","the","simplices","and","nodes",".","The","simplices","represent","a","indices","into","the","nodes","array",".","The","mesh","therefore","follows","a","datastructure","very","similar","to","a","graph",",","with","the","abstract","connectivity","between","nodes","stored","on","the","TriMesh","element","itself",",","the","node","positions","stored","on","a","Nodes","element","and","the","concrete","paths","making","up","each","triangle","generated","when","required","by","accessing","the","edgepaths",".","Unlike","a","Graph","each","simplex","is","represented","as","the","node","indices","of","the","three","corners","of","each","triangle","."]
code: def __init__(self, data, kdims=None, vdims=None, **params):
      if isinstance(data, tuple):
          data = data + (None,) * (3 - len(data))
          edges, nodes, edgepaths = data
      elif isinstance(data, type(self)):
          edges, nodes, edgepaths = data, data.nodes, data._edgepaths
      else:
          edges, nodes, edgepaths = data, None, None
  
      super().__init__(edges, kdims=kdims, vdims=vdims, **params)
      if nodes is None:
          if len(self) == 0:
              nodes = []
          else:
              raise ValueError(
                  "TriMesh expects both simplices and nodes "
                  "to be supplied."
              )
  
      if isinstance(nodes, self.node_type):
          pass
      elif isinstance(nodes, self.point_type):
          # Add index to make it a valid Nodes object
          nodes = self.node_type(
              Dataset(nodes).add_dimension(
                  "index", 2, np.arange(len(nodes))
              )
          )
      elif not isinstance(nodes, Dataset) or nodes.ndims in [2, 3]:
          if is_dataframe(nodes):
              coords = list(nodes.columns)[:2]
              index = nodes.index.name or "index"
              nodes = self.node_type(nodes, coords + [index])
          else:
              try:
                  points = self.point_type(nodes)
                  ds = Dataset(points).add_dimension(
                      "index", 2, np.arange(len(points))
                  )
                  nodes = self.node_type(ds)
              except Exception as e:
                  raise ValueError(
                      "Nodes argument could not be interpreted, expected "
                      "data with two or three columns representing the "
                      "x/y positions and optionally the node indices."
                  ) from e
      if edgepaths is not None and not isinstance(
          edgepaths, self.edge_type
      ):
          edgepaths = self.edge_type(edgepaths)
  
      self._nodes = nodes
      self._edgepaths = edgepaths
  
imports: from collections import defaultdict
  from types import FunctionType
  import numpy
  import pandas
  import param
  from ..core import Dataset, Dimension, Element2D
  from ..core.accessors import Redim
  from ..core.operation import Operation
  from ..core.util import is_dataframe, max_range, search_indices
  from .chart import Points
  from .path import Path
  from .util import circular_layout, connect_edges_pd, connect_tri_edges_pd, quadratic_bezier, split_path
label_desc: Use image node_id 1 to create a new TriMesh object from inherited base classes: Graph with example: obj = TriMesh(data, kdims, vdims)