# Source code for matplotlib.tri.triangulation

import numpy as np

[docs]class Triangulation:
"""
An unstructured triangular grid consisting of npoints points and
ntri triangles.  The triangles can either be specified by the user
or automatically generated using a Delaunay triangulation.

Parameters
----------
x, y : array-like of shape (npoints)
Coordinates of grid points.
triangles : integer array-like of shape (ntri, 3), optional
For each triangle, the indices of the three points that make
up the triangle, ordered in an anticlockwise manner.  If not
specified, the Delaunay triangulation is calculated.
mask : boolean array-like of shape (ntri), optional

Attributes
----------
edges : int array of shape (nedges, 2)
See ~.Triangulation.edges
neighbors : int array of shape (ntri, 3)
See ~.Triangulation.neighbors
mask : bool array of shape (ntri, 3)
is_delaunay : bool
Whether the Triangulation is a calculated Delaunay
triangulation (where *triangles* was not specified) or not.

Notes
-----
For a Triangulation to be valid it must not have duplicate points,
triangles formed from colinear points, or overlapping triangles.
"""
def __init__(self, x, y, triangles=None, mask=None):
from matplotlib import _qhull

self.x = np.asarray(x, dtype=np.float64)
self.y = np.asarray(y, dtype=np.float64)
if self.x.shape != self.y.shape or self.x.ndim != 1:
raise ValueError("x and y must be equal-length 1-D arrays")

self._edges = None
self._neighbors = None
self.is_delaunay = False

if triangles is None:
# No triangulation specified, so use matplotlib._qhull to obtain
# Delaunay triangulation.
self.triangles, self._neighbors = _qhull.delaunay(x, y)
self.is_delaunay = True
else:
# Triangulation specified. Copy, since we may correct triangle
# orientation.
self.triangles = np.array(triangles, dtype=np.int32, order='C')
if self.triangles.ndim != 2 or self.triangles.shape != 3:
raise ValueError('triangles must be a (?,3) array')
if self.triangles.max() >= len(self.x):
raise ValueError('triangles max element is out of bounds')
if self.triangles.min() < 0:
raise ValueError('triangles min element is out of bounds')

raise ValueError('mask array must have same length as '
'triangles array')

# Underlying C++ object is not created until first needed.
self._cpp_triangulation = None

# Default TriFinder not created until needed.
self._trifinder = None

[docs]    def calculate_plane_coefficients(self, z):
"""
Calculate plane equation coefficients for all unmasked triangles from
the point (x, y) coordinates and specified z-array of shape (npoints).
The returned array has shape (npoints, 3) and allows z-value at (x, y)
position in triangle tri to be calculated using
z = array[tri, 0] * x  + array[tri, 1] * y + array[tri, 2].
"""
return self.get_cpp_triangulation().calculate_plane_coefficients(z)

@property
def edges(self):
"""
Return integer array of shape (nedges, 2) containing all edges of

Each row defines an edge by it's start point index and end point
index.  Each edge appears only once, i.e. for an edge between points
*i*  and *j*, there will only be either *(i, j)* or *(j, i)*.
"""
if self._edges is None:
self._edges = self.get_cpp_triangulation().get_edges()
return self._edges

[docs]    def get_cpp_triangulation(self):
"""
Return the underlying C++ Triangulation object, creating it
if necessary.
"""
from matplotlib import _tri
if self._cpp_triangulation is None:
self._cpp_triangulation = _tri.Triangulation(
self._neighbors, not self.is_delaunay)
return self._cpp_triangulation

"""
Return an array of triangles that are not masked.
"""
else:
return self.triangles

[docs]    @staticmethod
def get_from_args_and_kwargs(*args, **kwargs):
"""
Return a Triangulation object from the args and kwargs, and
the remaining args and kwargs with the consumed values removed.

There are two alternatives: either the first argument is a
Triangulation object, in which case it is returned, or the args
and kwargs are sufficient to create a new Triangulation to
return.  In the latter case, see Triangulation.__init__ for
the possible args and kwargs.
"""
if isinstance(args, Triangulation):
triangulation, *args = args
else:
x, y, *args = args

# Check triangles in kwargs then args.
triangles = kwargs.pop('triangles', None)
from_args = False
if triangles is None and args:
triangles = args
from_args = True

if triangles is not None:
try:
triangles = np.asarray(triangles, dtype=np.int32)
except ValueError:
triangles = None

if triangles is not None and (triangles.ndim != 2 or
triangles.shape != 3):
triangles = None

if triangles is not None and from_args:
args = args[1:]  # Consumed first item in args.

# Check for mask in kwargs.

triangulation = Triangulation(x, y, triangles, mask)
return triangulation, args, kwargs

[docs]    def get_trifinder(self):
"""
Return the default :class:matplotlib.tri.TriFinder of this
triangulation, creating it if necessary.  This allows the same
TriFinder object to be easily shared.
"""
if self._trifinder is None:
# Default TriFinder class.
from matplotlib.tri.trifinder import TrapezoidMapTriFinder
self._trifinder = TrapezoidMapTriFinder(self)
return self._trifinder

@property
def neighbors(self):
"""
Return integer array of shape (ntri, 3) containing neighbor triangles.

For each triangle, the indices of the three triangles that
share the same edges, or -1 if there is no such neighboring
triangle.  neighbors[i, j] is the triangle that is the neighbor
to the edge from point index triangles[i,j] to point index
triangles[i,(j+1)%3].
"""
if self._neighbors is None:
self._neighbors = self.get_cpp_triangulation().get_neighbors()
return self._neighbors

"""
Set or clear the mask array.  This is either None, or a boolean
array of shape (ntri).
"""
else:
raise ValueError('mask array must have same length as '
'triangles array')

# Set mask in C++ Triangulation.
if self._cpp_triangulation is not None: